Edit report at https://bugs.php.net/bug.php?id=65485&edit=1

 ID:                 65485
 Updated by:         ras...@php.net
 Reported by:        stephane at it-asia dot com
 Summary:            Cast gives different values in one way or the other
 Status:             Not a bug
 Type:               Bug
 Package:            *General Issues
 Operating System:   Windows 8 & Mint Maya
 PHP Version:        5.4.18
 Block user comment: N
 Private report:     N

 New Comment:

var_dump() is giving you the real value based on your precision setting. If you 
add ini_set('precision',32); to the top of your test script, you will see:

float(3947.9999999999995452526491135359)
float(3948)
int(3947)
int(3948)

And this is exactly the same as C and Python.

And no, there won't be a money type in PHP. This is trivial to do in user space.


Previous Comments:
------------------------------------------------------------------------
[2013-08-20 13:44:38] stephane at it-asia dot com

Hi Rasmus

Good to find you here.

I understand you follow the C engine. But in C , you have 39.48*100 =  
3947.9999999999995  and in Python, you have 39.48*100 = 3947 when PHP shows 
3948 . 

PHP makes a few more things than that. It is not exactly the same. 

More, when I make tests in the php.ini to increase the precision to 20, the 
float → string gives  3947.9999999999995453  (and 
3947.99999999999954525264911353588104248046875 for a precision of 100)

And the vardump doesn't really give the real full value : it is confusing. 
var_dump (39.48 * 100) => float(3948)   => this is not the real value. 

Better example: 
$d1 = 39.48 * 100;
$d2 = 3948.0;
$i1 = (int) $d1;
$i2 = (int) $d2;

var_dump($d1);
var_dump($d2);
var_dump($i1);
var_dump($i2);

it returns float(3948) float(3948) int(3947) int(3948) 
it should return float(3947.99999999999954525264911353588104248046875) 
float(3948) int(3947) int(3948) 

Can we have a more precise vardump, so we know exactly what happens? I lost 
time because of that. If the vardump gave me  
3947.99999999999954525264911353588104248046875 this morning, I would have 
understood directly what happened. But it gave me 3948, and it is not the real 
value.

I was accustomed to see PHP to provide easy and direct solutions. I don't know 
why, but I was totally convinced we found a way to go over this kind of 
problems. Obviously, talk to any developer around you, nobody never expects to 
see 3948 become 3947 after a cast when a decimal number was involved 100 lines 
before.

In VB6, the result is always 3948 (even for double → int casting ), and they 
have a currency type. SQL server created a "money" type to avoid this kind of 
problems ( http://technet.microsoft.com/en-us/library/ms179882.aspx ). I hate 
saying that, but these guys found  solutions when we still didn't.

Rasmus, can I suggest to consider a new type for “money” treatments? So we 
will still have the normal int and float that match the C standard, and we will 
have something to make time and go directly to the point . For example, a 
“64-bit (8-byte) numbers in an integer format, scaled by 10,000 to give a 
fixed-point number with 15 digits to the left of the decimal point and 4 digits 
to the right”. So (int)39.48*100 will never return 3947 anymore, and brutal 
and lemmings coders like me will make better accountancy software.

Thanks :)

------------------------------------------------------------------------
[2013-08-20 12:08:41] ras...@php.net

You mean like this in Python:

>>> int(39.48*100)
3947
>>> "%s" % (39.48 * 100)
'3948.0'

This is not PHP-specific in any way.

------------------------------------------------------------------------
[2013-08-20 11:10:22] ni...@php.net

@stephane: Float-to-integer casts are the same across all languages, at least 
as far as I am aware. They are always truncations (i.e. they just take the 
integer portion of the number and drop the rest. For positive numbers 
truncation and floor are the same).

I am sorry if this does not match your expectations, but this is standard 
behavior that everyone uses and we will not deviate from it.

How integer to float casts work is documented here: 
http://www.php.net/manual/en/language.types.integer.php#language.types.integer.casting

 > When converting from float to integer, the number will be rounded towards 
 > zero.

See also the warning on that page, which deals with exactly your situation.

In your case, what you are probably looking for is just the round() function, 
which will do a round towards the closest integer, rather than a round towards 
zero (truncation).

------------------------------------------------------------------------
[2013-08-20 10:14:59] stephane at it-asia dot com

I don't find sane to see a 3948 float value going to 3947 after an int casting, 
I really, really don't find it sane.

I don't find sane as well to see the same number ending with different value 
after several casts. The cast doesn't provide the same precision when it is one 
case, ot the other. One uses floor(), when the other uses (round). And to be 
honest, one is obviously right, when the other is obviously wrong. 

Please provide examples where the float -> string -> int value doesn't give 
satisfaction. 

Because for me, the float -> int cast NEVER gives satisfaction when there's a 
decimal number involved.

The right way is the way giving the right solution, It is not the one giving 
the wrong answer for the only pleasure to match impossibility theory.

In every case, please add to the documentation that the float -> int cast gives 
stupid results because ti tends to respect a theoric lack orp precision, when 
the float -> string cast tends to provide a more acceptable answer.

------------------------------------------------------------------------
[2013-08-20 09:53:17] ni...@php.net

I think you misunderstood how the casts work:

An (int) cast rounds DOWN to the nearest integer (in all sane languages).

A (string) cast just converts the float to a string representation. This 
representation has the precision specified with the "precision" ini setting.

So what you are comparing here is a down-round ( floor($f) ) with a 
round-with-precision ( round($f, $prec) ). Of course the result will be 
different.

------------------------------------------------------------------------


The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at

    https://bugs.php.net/bug.php?id=65485


-- 
Edit this bug report at https://bugs.php.net/bug.php?id=65485&edit=1

Reply via email to