3

In one of my projects I originally started using floats, but after a bit I realized that its precision isn't exactly how I want it to be. I have automated system that adds value of X coins every second, and given that that float is at big number (more than 2 million), it just do not add such small value to it anymore.

So my next call was to use double instead of float. After changing everything from float to double, I realized that I store values in PlayerPrefs, in which I guess I cannot store doubles? So now I'm a bit confused.

How do I pull out existing float information from PlayerPrefs and put that floats value in double, and then save that double value in PlayerPrefs? A bit confusing for me, so I'm seeking help.

Here's part of my code:

// old
// public float totalP;

// new    
public double totalP;

void OnEnable()
{

    if (!PlayerPrefs.HasKey ("game_totalP")) 
    {
        PlayerPrefs.SetFloat ("game_totalP", totalP);
    } 
    else 
    {
        totalP = PlayerPrefs.GetFloat ("game_totalP");
    }        
}

Any help would be appreciated!

PassetCronUs
  • 447
  • 4
  • 11
Arthur Belkin
  • 125
  • 1
  • 10

3 Answers3

4

You can encode the double as two int and store and retrieve those.

To store:

var storeBytes = BitConverter.GetBytes(totalP);
var storeIntLow = BitConverter.ToInt32(storeBytes, 0);
var storeIntHigh = BitConverter.ToInt32(storeBytes, 4);
PlayerPrefs.SetInt("game_totalP_low", storeIntLow);
PlayerPrefs.SetInt("game_totalP_high", storeIntHigh);

To retrieve:

var retrieveBytes = new byte[8];
Array.Copy(BitConverter.GetBytes(storeIntLow), retrieveBytes, 4);
Array.Copy(BitConverter.GetBytes(storeIntHigh), 0, retrieveBytes, 4, 4);
totalP = BitConverter.ToDouble(retrieveBytes, 0);
jdphenix
  • 15,022
  • 3
  • 41
  • 74
1

You can save double with PlayerPrefs. The appropriate PlayerPrefs function for this is PlayerPrefs.SetString not PlayerPrefs.GetFloat.

The double to save:

public double totalP;

Save:

Convert it to string

string totalPStr = totalP.ToString();

Save as string

PlayerPrefs.SetString("game_totalP", totalPStr)

Load:

Load as string

string tempPStr = PlayerPrefs.GetString("game_totalP");

Convert the string to double

totalP = double.Parse(tempPStr, System.Globalization.CultureInfo.InvariantCulture);

If you run into scientific notation issues with totalP.ToString(), change that to totalP.ToString("F6"). Finally, I think you should abandon PlayerPrefs and use serialization to save your data. See this post for more information.

Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Thanks a lot for clarification! I think I see how it works now. Next question is, how do I retrieve old data and replace it with string? As in, I used to do this: if (!SecurePlayerPrefs.HasKey ("game_totalP")) { SecurePlayerPrefs.SetFloat ("game_totalP", totalP); } else { totalP= SecurePlayerPrefs.GetFloat ("game_totalP"); } how do I make it so if there isn't anything in playerPrefs for string, replace it with existing float values? – Arthur Belkin Apr 01 '18 at 03:37
  • I'm assuming I have to convert float to double (or string)? – Arthur Belkin Apr 01 '18 at 03:39
  • 1
    Did you even read the answer? I used `PlayerPrefs.SetString` and `PlayerPrefs.GetString` **instead** of `PlayerPrefs.SetFloat` and `SecurePlayerPrefs.GetFloat` but your comment is showing something different. Don't use `SetFloat` or `GetFloat` for this. Please read this answer again. – Programmer Apr 01 '18 at 06:00
  • Yes I did? You did not understand my second question then. I got your initial answer and I'm thankful for it. What I meant to say is that I have existing data in SetFloat/GetFloat that I would like to use in GetString, if that makes sense? As in, if I will go with current code, it will all start from nothing. I want it to check if PlayerPrefs.Haskey returns false from the value of "game_totalP" of string value, and repalaces it with existing value of float, but in string instead. – Arthur Belkin Apr 01 '18 at 08:33
  • Ok I get it but you are using **"game_totalP"** as the key. If you use `PlayerPrefs.SetString("game_totalP",totalP)`, it will overwrite the data you stored with `SecurePlayerPrefs.SetFloat("game_totalP", totalP);` and this is also true vice-versa so it doesn't make sense to try to load float and string with the-same key. There is no need to do that. Do you understand me? – Programmer Apr 01 '18 at 08:41
  • If you still want to do that regardless of what I said above then use the `ToString()` like I did in the answer to convert the `float` to `string` then save it as `string` with `SetString`. Load float: `float fData = PlayerPrefs.GetFloat("game_totalP");` then save that float as string: `PlayerPrefs.SetString("game_totalP", fData.ToString());`. – Programmer Apr 01 '18 at 09:08
  • I did not know it was that simple. I thought if you record float in PlayerPrefs, you will only be able to access it via GetFloat, and if I did GetString, it will return 0 or false. Thanks for clarification. Appreciate your help. You rock!! <3 – Arthur Belkin Apr 01 '18 at 22:08
  • Nope. Remember that you can store `int` in a `string`. You can store `float` in a `string` and finally, you can store `double` in a `string`. That's why I used `string`. Save as string, load as string then convert the string back to `float` or `double`.Simple as that. You are welcome! – Programmer Apr 01 '18 at 22:19
  • 1
    Thanks a lot!! Glad I can finally understand how it actually works. – Arthur Belkin Apr 01 '18 at 22:23
1

I can not just add a comment but I rewrote what jdphenix created for the PlayerPrefsX extension that is out there. Most people who use playerprefs would just use it instead so here:

public static double GetDouble(string key)
{
    var retrieveBytes = new byte[8];
    Array.Copy(BitConverter.GetBytes(PlayerPrefs.GetInt(key+"_doubleLowBits")), retrieveBytes, 4);
    Array.Copy(BitConverter.GetBytes(PlayerPrefs.GetInt(key+"_doubleHighBits")), 0, retrieveBytes, 4, 4);
    return BitConverter.ToDouble(retrieveBytes, 0);
}

public static void SetDouble(string key, double defaultValue)
{
    var storeBytes = BitConverter.GetBytes(defaultValue);
    var storeIntLow = BitConverter.ToInt32(storeBytes, 0);
    var storeIntHigh = BitConverter.ToInt32(storeBytes, 4);
    PlayerPrefs.SetInt(key+"_doubleLowBits", storeIntLow);
    PlayerPrefs.SetInt(key+"_doubleHighBits", storeIntHigh);
}

This is for anyone else who got here from google trying to find this answer...

user2455808
  • 95
  • 1
  • 1
  • 9