|
rounding not working as expected? |
Posted by Chris Chan on Apr-25-2008 04:28 |
|
Strange problem, when I use {value|2} I get the correct value displayed on the graph, e.g. 1.25, but when I use {value|1} I get 1.2 instead of 1.3.
I understand that Chartdirector uses symmetric arithmetic rounding, so I would assume it rounds up, and it seems to do it correctly for all my other charts, but this one is acting weird.
I can manually round the data before passing into Chartdirector, but I'm wondering why it would do this? The data being passed to the chart is just returned from a getRow database call and the fields being pulled from the table are decimal(19,16) values. And the only math being performed on the field is a multiplication by 100.
Any ideas? |
Re: rounding not working as expected? |
Posted by Peter Kwan on Apr-25-2008 15:33 |
|
Hi Chris,
This is not strange and is quite common. For example, if you data value is 1.24999999999999999999999, then when rounded to 2 decimal places, it is 1.25, and when rounded to 1 decimal place, it is 1.2. If you print the value out using your programming language, it probably will be printed as 1.25. (Many programming language will round the number to say 6 decimal places for printing, so the above number may be printed as 1.25.)
If you are using PHP, there is no such thing as decimal(19,16) values. So there are a number of conversions - the database driver may convert from decimal(19,16) to the PHP supported data type (IEEE 754 64-bit floating point value). Then you may have multiply the number by 100. There may be other implicit operations (such as the conversion operation above) that you are not aware of, but which may affect the precision of the number by say 0.00000000001%. A double precision floating point number has a precision of around 17 decimal places, and a change in least significant digit can make the difference between rounding to 1.2 or 1.3.
To analysis the exact cause of the problem, you would need to know the exact value of your number in PHP.
In general, in PHP, is it not possible to have absolutely accurate arithmatic for floating point numbers. The precision is limited to 64-bit IEEE 754. To perform precise arithmatic, you need to use a data type such as decimal, which is not supported by PHP. So I suggest you may round the number and perform the multiplication by 100 using SQL. The SQL is executed by your database, not by PHP. As you have mentioned in your message , your database does support decimal, and so it should perform the arithmatic precisely.
Hope this can help.
Regards
Peter Kwan |
Re: rounding not working as expected? |
Posted by Chris Chan on Apr-26-2008 00:40 |
|
Hi Peter, thanks for the reply. I tried this even:
$data[0]=1.05;
$data[1]=1.15;
$data[2]=1.25;
$data[3]=1.35;
$layer->addDataSet($data, 0x00CCFF);
$layer->setAggregateLabelFormat("{value|1}%");
And it looks like the first picture below. Only two of the values are being rounded up.
But setting:
$layer->setAggregateLabelFormat("{value|2}%");
shows it correct to two decimals as in the second picture.
|
Re: rounding not working as expected? |
Posted by Peter Kwan on Apr-26-2008 02:45 |
|
Hi Chris,
The reason is that your data value is not 1.15. It is 1.149999999999999......
What you have written is "source code". It is only for human reading. It is not what the computer is using. The PHP interpreter will need to translate the 1.15 into IEEE 754 floating point number when the script is executed.
However, it is not possible to exactly translate 1.15 to a floating point number. It needs an infinite number of decimal places in binary. (Just like the fraction 1/3 needs infinite number of decimal place as 0.3333333333...., the number 1.15 needs infinte number of decimal places when represented using the binary numbering format.)
You can try the following code:
$data = 1.15;
$x = $data * 100 - 115;
print_r($x);
If the $data is exactly 1.15, the $x should be 0. If the $x is a negative number, it means the $data is less than 1.15. It is probably 1.14999999999999. So $data should round to 1.1.
I am afraid if you prefer to "round up", you may need to add a smaller number to all your $data to bias it upwards (so your 1.15 will be 1.15000000000000001 in binary, instead of 1.1499999999999999999). I think a number like 1E-12 is sufficient.
(Adding a small bias should not be a concern, as even the original numbers are inaccurate anyway.)
Regards
Peter Kwan |
Re: rounding not working as expected? |
Posted by Geoff Groskreutz on Sep-23-2014 07:10 |
|
I also have noticed this issue recently, and I understand your reasoning, but I believe Chart Director's rounding should use a BigDecimal conversion before rounding, so that all rounding is consistent. From looking at other Java posts on the subject, it looks like you would need to convert to a BigDecimal by using its static valueOf method before performing the formatting. So, that it would convert the double using string conversion into a BigDecimal which will convert it back to the original decimal correctly, then perform the rounding on this. Of course this could still be a potential issue with calculations that are performed on the double before conversion, but at least it would be more consistent when displaying the label of a non-aggregated value.
What you could do is provide us with a format option that allows us to decide how we want the rounding to be applied when it comes to conversion of doubles to decimal values. |
Re: rounding not working as expected? |
Posted by Peter Kwan on Sep-23-2014 23:50 |
|
Hi Geoff,
We will definitely look into this issue in more details.
In the mean time, one work around is to simply multiple the number by 1.00000000000001
before formatting. For example:
{={value}*1.00000000000001|1}
The double precision number has 53 bits precision (the other 11 bits are the exponent and
other book keeping purpose), which is equivalent to around 17 decimal digit precision. If we
multiply the number by 1.00000000000001 (15 decimal digits), it should increase the number
by a very small amount that it has no effect on rounding except for numbers where the last
digit is very near to 5 (such as 1.1499999999999999999999999).
I have tried myself. If I used:
double x = 1.14999999999999999999999;
ChartDirector will round the above number to 1.1, while the BigDecimal method will round it
to 1.2. If the BigDecimal method is more preferable, then multiplying by a number just larger
than 1 should have similar effect. The drawback of this method is that is reduces the
effective precision to something like 14 decimal digits, but I think for most charting and
labelling usage it should be sufficient.
Regards
Peter Kwan |
Re: rounding not working as expected? |
Posted by Geoff Groskreutz on Sep-25-2014 09:52 |
|
I guess this is a sufficient work around for now. I'll let our client know, how he can
effectively emulate the Java BigDecimal formatting with your multiply solution.
Thanks,
Geoff |
|