Difference between revisions of "Proper Money Handling (JSON-RPC)"

From Bitcoin Wiki
Jump to: navigation, search
(But if a library only shows 4 or 6 decimal places, bitcoind *can't* figure out what the lost precision was. And while bitcoind might correct for buggy input beyond 8 decimal places, bitcoind is only an *implementation*, not a protocol spec...)
(python-json-rpc example)
Line 26: Line 26:
  
 
== Python ==
 
== Python ==
Pass the parse_float arguments to [http://docs.python.org/library/json.html python's JSON parsing routines] to parse JSON values into Decimal:
+
If you are using [http://json-rpc.org/wiki/python-json-rpc python-json-rpc], you should convert its floating point values to and from the python Decimal type, specifying 8 digits of precision.  For example:
 +
  import decimal
 +
  from jsonrpc import ServiceProxy
 +
 +
  access = ServiceProxy("http://user:password@127.0.0.1:8332")
 +
  info = access.getinfo()
 +
  decimal.setcontext(decimal.Context(prec=8))
 +
  balance = decimal.Decimal("%.8f"%info['balance'])
 +
  amount_to_send = balance / decimal.Decimal('2')
 +
  access.sendtoaddress('...bitcoin address...', float(amount_to_send))
 +
 
 +
If you are using the standard json library, you can use the parse_float arguments to [http://docs.python.org/library/json.html python's JSON parsing routines] to parse JSON values into Decimal and use the DecimalEncoder class to write out Decimal values:
 
   import decimal
 
   import decimal
 
   import json
 
   import json

Revision as of 01:59, 4 March 2011

Overview

The original bitcoin client stores all bitcoin values as 64-bit integers, with 1 BTC stored as 100,000,000 (one-hundred-thousand of the smallest possible bitcoin unit). Values are expressed as double-precision Numbers in the JSON API, with 1 BTC expressed as 1.00000000

If you are writing software that uses the JSON-RPC interface you need to be aware of possible floating-point conversion issues. You, or the JSON library you are using, should convert amounts to either a fixed-point Decimal representation (with 8 digits after the decimal point) or a 64-bit integer representation.

Improper value handling can lead to embarrassing errors; for example, if you truncate instead of doing proper rounding and your software will display the value "0.1 BTC" as "0.09999999 BTC" (or, worse, "0.09 BTC").

The rest of this page gives sample code for various JSON libraries and programming languages.

ECMAScript

The ECMAScript Number type is double-precision floating point; ECMAScript does not have an integral numeric type.

JavaScript experts: what is best practice? Convert to an integral-like-double, or just do proper rounding on display and do all calculations using doubles?

  • This is not ECMAScript specific: all program internals should use raw/base bitcoin amounts, never floats.

C/C++

C/C++ JSON libraries return the JavaScript Number type as type 'double'. To convert, without loss of precision, from a double to a 64-bit integer multiply by 100,000,000 and round to the nearest integer:

 double dValue = ...from JSON library...;
 long long int64Value;
 if (dValue > 0) { int64Value = (long long)(dValue * 10e8 + 0.5);
 else { int64Value = (long long)(dValue * 10e8 - 0.5);

To convert to a JSON value divide by 100,000,000.0, and (somehow) ensure your JSON implementation rounds to 8 decimal places:

 double dValue = (double) int64Value / 10e8

Python

If you are using python-json-rpc, you should convert its floating point values to and from the python Decimal type, specifying 8 digits of precision. For example:

 import decimal
 from jsonrpc import ServiceProxy

 access = ServiceProxy("http://user:password@127.0.0.1:8332")
 info = access.getinfo()
 decimal.setcontext(decimal.Context(prec=8))
 balance = decimal.Decimal("%.8f"%info['balance'])
 amount_to_send = balance / decimal.Decimal('2')
 access.sendtoaddress('...bitcoin address...', float(amount_to_send))

If you are using the standard json library, you can use the parse_float arguments to python's JSON parsing routines to parse JSON values into Decimal and use the DecimalEncoder class to write out Decimal values:

 import decimal
 import json
 
 # From http://stackoverflow.com/questions/1960516/python-json-serialize-a-decimal-object
 class DecimalEncoder(json.JSONEncoder):
   def _iterencode(self, o, markers=None):
     if isinstance(o, decimal.Decimal):
       return (str(o) for o in [o])
     return super(DecimalEncoder, self)._iterencode(o, markers)
 
 decimal.setcontext(decimal.Context(prec=8))
 print json.dumps(decimal.Decimal('10.001'), cls=DecimalEncoder)
 print json.dumps({ "decimal" : decimal.Decimal('1.1'), "float" : 1.1, "string" : "1.1" }, cls=DecimalEncoder)
 print json.loads('{"amount": 0.333331}', parse_float=decimal.Decimal)

Output is:

 10.001
 {"decimal": 1.1, "float": 1.1000000000000001, "string": "1.1"}
 {u'blaa': Decimal('0.333331')}