error
|
|
March 11, 2011, 10:55:52 PM |
|
You can do everything in GMP except decode the JSON, which is what I think OP was complaining about.
|
3KzNGwzRZ6SimWuFAgh4TnXzHpruHMZmV8
|
|
|
genjix (OP)
Legendary
Offline
Activity: 1232
Merit: 1076
|
|
March 11, 2011, 10:57:50 PM |
|
|
|
|
|
ShadowOfHarbringer
Legendary
Offline
Activity: 1470
Merit: 1006
Bringing Legendary Har® to you since 1952
|
|
March 11, 2011, 10:59:19 PM |
|
You can do everything in GMP except decode the JSON, which is what I think OP was complaining about.
This is a misunderstanding. I only meant do the calculations in GMP, and only convert from/to string on input/output . Ah sorry then, my mistake.
|
|
|
|
genjix (OP)
Legendary
Offline
Activity: 1232
Merit: 1076
|
|
March 11, 2011, 11:00:04 PM |
|
You can do everything in GMP except decode the JSON, which is what I think OP was complaining about.
No, I was complaining about the fact that the Bitcoin JSON-RPC Api uses numbers (that nearly all libraries convert to floats and have no option to change) when using strings would only be minorly impactful.
|
|
|
|
ShadowOfHarbringer
Legendary
Offline
Activity: 1470
Merit: 1006
Bringing Legendary Har® to you since 1952
|
|
March 11, 2011, 11:07:10 PM |
|
OK, i have found the base for my allegations about dangers of using floats: Warning Floating point precision
Floating point numbers have limited precision. Although it depends on the system, PHP typically uses the IEEE 754 double precision format, which will give a maximum relative error due to rounding in the order of 1.11e-16. Non elementary arithmetic operations may give larger errors, and, of course, error progragation must be considered when several operations are compounded.
Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9999999999999991118....
So never trust floating number results to the last digit, and never compare floating point numbers for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.
If this doesn't shout "floats are bad", then i don't know what else to say about that. So everything i said was true. "Precision depends on the system". So it may be different on 32bit and 64bit systems, and perhaps even on windows / Linux / different types of Unix/BSD.
|
|
|
|
ShadowOfHarbringer
Legendary
Offline
Activity: 1470
Merit: 1006
Bringing Legendary Har® to you since 1952
|
|
March 11, 2011, 11:15:04 PM |
|
You can still use bcmath, may be faster than working strings: http://www.php.net/manual/en/book.bc.phpAlso, somebody on the forums told me that there are many other libraries supporting arbitrary precision mathematics for PHP, but i don't know them myself because i never needed that.
|
|
|
|
Luke-Jr
Legendary
Offline
Activity: 2576
Merit: 1186
|
|
March 12, 2011, 02:05:17 AM |
|
This isn't the only (nor biggest) problem with JSON-RPC. Help create a new standard protocol fixing this and other problems: https://en.bitcoin.it/wiki/Wallet_protocol
|
|
|
|
genjix (OP)
Legendary
Offline
Activity: 1232
Merit: 1076
|
|
March 12, 2011, 04:09:03 AM |
|
import urllib import decimal import json
class JSONRPCException(Exception): def __init__(self, rpcError): Exception.__init__(self) self.error = rpcError class ServiceProxy(object): def __init__(self, serviceURL, serviceName=None): self.__serviceURL = serviceURL self.__serviceName = serviceName
def __getattr__(self, name): if self.__serviceName != None: name = "%s.%s" % (self.__serviceName, name) return ServiceProxy(self.__serviceURL, name)
def __call__(self, *args): postdata = json.dumps({"method": self.__serviceName, 'params': args, 'id':'jsonrpc'}) respdata = urllib.urlopen(self.__serviceURL, postdata).read() resp = json.loads(respdata, parse_float=decimal.Decimal) if resp['error'] != None: raise JSONRPCException(resp['error']) else: return resp['result']
This should work in Python is anybody ever needs it. I've updated the wiki, https://en.bitcoin.it/wiki/API_tutorial_%28JSON-RPC%29#PythonIt's based off the old lib.
|
|
|
|
genjix (OP)
Legendary
Offline
Activity: 1232
Merit: 1076
|
|
March 12, 2011, 05:52:18 AM Last edit: March 12, 2011, 02:10:54 PM by genjix |
|
Anyone that prefers a saner API (because they're using PHP) should check out my branch, https://github.com/genjix/bitcoin/tree/strrpcValues are returned as int64 strings. function numstr_to_internal($numstr) { return bcmul($numstr, pow(10, 8), 0); } function internal_to_numstr($num, $precision=8) { $repr = gmp_strval($num); $repr = bcdiv($repr, pow(10, 8), $precision); # now tidy output... # trim trailing 0s $repr = rtrim($repr, '0'); # and a trailing . if it exists $repr = rtrim($repr, '.'); return $repr; }
Those are the 2 functions I use to convert from internal values to display/user input values (numstr). I updated the wiki, https://en.bitcoin.it/wiki/API_tutorial_%28JSON-RPC%29#PHP
|
|
|
|
|
Gavin Andresen
Legendary
Offline
Activity: 1652
Merit: 2300
Chief Scientist
|
|
March 12, 2011, 03:24:51 PM Last edit: March 14, 2011, 11:49:04 AM by gavinandresen |
|
Is there a PHP implementation that does not use double-precision floating point? After doing a little googling I couldn't figure out the answer to that. I will be MUCH more sympathetic to changing the JSON-RPC api if there is. And mizerydearia: re: the witcoin issue: You say: "I see the transaction as 0.94 However, http://json-rpc.org/ retrieves the data as 0.93999999999999994671" So why when you display that value are you truncating it instead of rounding it to 8 decimal places? For example: > php -r "printf('%.8f', 0.94);" #CORRECT 0.94000000 > php -r "printf('%.16f', 0.94);" #WRONG 0.9399999999999999
... or to convert to an integer-number-of-base-unit: > php -r '$val=0.94; printf("%d", round(1e8*$val));' 94000000
All of that assume that your php support double-precision floating point, which brings me back to my question: are there any php implementations that do not?
|
How often do you get the chance to work on a potentially world-changing project?
|
|
|
genjix (OP)
Legendary
Offline
Activity: 1232
Merit: 1076
|
|
March 12, 2011, 03:57:26 PM |
|
<?php $json = '{"a":1,"b":2,"c":3,"d":4.08,"e":5}'; var_dump(json_decode($json)); ?>
Output: object(stdClass)#1 (5) { ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) ["d"]=> float(4.08) ["e"]=> int(5) }
Why don't why put in a new number like nAPIVersion and call the current API 0. Then we can make nAPIVersion 0.5 supporting the int64 with appended .0 as luke-jr has done in his gitorious branch. Then I want to make an API version 1.0 which puts in a strict naming convention for the methods (getblaa, action instead of the current messy names) and use namespaces.
|
|
|
|
Gavin Andresen
Legendary
Offline
Activity: 1652
Merit: 2300
Chief Scientist
|
|
March 12, 2011, 04:12:07 PM |
|
Then we can make nAPIVersion 0.5 supporting the int64 with appended .0 as luke-jr has done in his gitorious branch.
That's just dumb. If the problem is jsonrpc-supporting-environments that don't support double-precision floats, then multiplying and slapping a zero on the end won't fix the problem-- you'll just either lose precision as it gets squeezed into a 32-bit float or get a 32-bit-integer-overflow error.
|
How often do you get the chance to work on a potentially world-changing project?
|
|
|
genjix (OP)
Legendary
Offline
Activity: 1232
Merit: 1076
|
|
March 12, 2011, 04:37:03 PM |
|
You're right. In that case, it seems that going with strings is the only option. Still it might be a good idea to have API versions (bitcoin.getinfo()["rpcversion"]) and take an optional -rpcversion command line argument. When you create a new API, then the old one is deprecated for at least one release cycle, allowing compatibility. When a new RPC version is created (lets call new version 1 and old version 0 for example) then: bitcoind The RPC version (0) in this release is DEPRECATED! Re-run bitcoind -rpcversion=1 for the new version. RPC changes can be found at http://bitcoin.org/rpcapi.htmlbitcoind stop bitcoind -rpcversion=1 bitcoind getinfo ... "rpcversion": 1 ... The policy would be to deprecate only when the API becomes backwards incompatible. Otherwise it's silently upgraded and becomes the default.
|
|
|
|
ShadowOfHarbringer
Legendary
Offline
Activity: 1470
Merit: 1006
Bringing Legendary Har® to you since 1952
|
|
March 12, 2011, 05:29:31 PM |
|
You're right. In that case, it seems that going with strings is the only option.
All serious institutions (like banks) never use floats for currency calculations because they know the dangers. If we want to be seen as "serious", float is not an option.
|
|
|
|
genjix (OP)
Legendary
Offline
Activity: 1232
Merit: 1076
|
|
March 12, 2011, 05:31:43 PM |
|
You're right. In that case, it seems that going with strings is the only option.
All serious institutions (like banks) never use floats for currency calculations because they know the dangers. If we want to be seen as "serious", float is not an option. Yep but it's a problem with the json decoding in PHP/Perl/Python. The actual precision for the number in a JSON is accurate AFAIK.
|
|
|
|
Gavin Andresen
Legendary
Offline
Activity: 1652
Merit: 2300
Chief Scientist
|
|
March 12, 2011, 05:35:32 PM |
|
All serious institutions (like banks) never use floats for currency calculations because they know the dangers. If we want to be seen as "serious", float is not an option.
You know, I was looking at the PayPal payment API yesterday, and $1.01 is sent as... 1.01 "transmitting" != "calculations"
|
How often do you get the chance to work on a potentially world-changing project?
|
|
|
ShadowOfHarbringer
Legendary
Offline
Activity: 1470
Merit: 1006
Bringing Legendary Har® to you since 1952
|
|
March 12, 2011, 06:24:26 PM |
|
All serious institutions (like banks) never use floats for currency calculations because they know the dangers. If we want to be seen as "serious", float is not an option.
You know, I was looking at the PayPal payment API yesterday, and $1.01 is sent as... 1.01 "transmitting" != "calculations" Of course you're right. I was merely stating the fact.
|
|
|
|
j16sdiz
Newbie
Offline
Activity: 37
Merit: 0
|
|
March 14, 2011, 07:50:25 AM |
|
.... > php -r "printf('%.8f', 0.94);" #CORRECT 0.94000000 > php -r "printf('%.16f', 0.94);" #WRONG 0.9399999999999999
.... It does not work that way .. printf("%.8f") behaviour is inconsistent across platform. If you don't believe, try -- some platform give -999.03, some give -999.04. Of course, you can fix this.... in your code. (But never expect all 3rd party apps do it right)
|
|
|
|
Gavin Andresen
Legendary
Offline
Activity: 1652
Merit: 2300
Chief Scientist
|
|
March 14, 2011, 11:55:07 AM |
|
It does not work that way .. printf("%.8f") behaviour is inconsistent across platform. If you don't believe, try -- some platform give -999.03, some give -999.04. I tried it... got -999.04 on my Mac and Linux machines. What platform gives the wrong answer?
|
How often do you get the chance to work on a potentially world-changing project?
|
|
|
|