Why php rounds this number? [duplicate] - php

This question already has answers here:
Set precision for a float number in PHP
(5 answers)
Closed 3 years ago.
I have an app that getting Ethereum balance by address. The app receives balance from API and then puts it to the database. Balance comes in hex-integer:
$balance = $response->getBody(); //0x1e1e83d93bb6ebb88bbaf
Then I convert it to the WEI integer:
$hexInt = BC::hexdec($balance); // WEI "2275742359981542120930223"
And then I need to Convert WEI to ETH:
return $balance / '1000000000000000000';
If calculate it, it will be 2275742.359981542120930223, but PHP converts it to 2275742.3599815. As you see, php rounds this number after division. Why? And how can I get right result?

This happens because of the implicit casts. The division returns a float. Floats are not exact values. They have a precision. You can use number_format() to specify the amount of decimals/precision for output, but the float might not provide the necessary precision. Here are ini options for it.
One solution is using bcdiv() with the expected precision. Or you write your own formatting method using string functions:
$balance = '2275742359981542120930223';
$result = $balance / '1000000000000000000';
var_dump($result);
var_dump(number_format($result, 18, '.', ''));
var_dump(bcdiv($balance, '1000000000000000000', 18));
function formatETH(string $value, int $factor = 18) {
return substr($value, 0, -$factor).'.'.substr($value, -$factor);
}
var_dump(formatETH($balance));
Output:
float(2275742.3599815)
string(26) "2275742.359981541987508535"
string(26) "2275742.359981542120930223"
string(26) "2275742.359981542120930223"

Since your dealing with big numbers, and using https://github.com/krowinski/bcmath-extended you can use the BC function for division. That bcmath-extended has a wrapper for it (bcdiv), so try:
return BC::div($balance, '1000000000000000000');

Related

how to set the min and max to 2 decimal places in php [duplicate]

This question already has answers here:
Show a number to two decimal places
(25 answers)
Closed 2 years ago.
<?php
$result = array_filter($_POST);
if(!empty($result)){
echo ((max($result)+min($result))/2)*1.2 ."|".((max($result)+min($result))/2)*1.3;
}
?>
hi all..how to set min and max result to 2 decimals places for the code above? actually the result will appears in input type text after the process complete
Use number_format function in PHP.
number_format ( float $number [, int $decimals = 0 ] ) : string
For more information see here
$yourNumber = 1235.343;
echo number_format ($yourNumber, 2);
// Will output 1,235.34 (with two decimals, specified in number_format as second paramter.
Edit: Max / Min functions return mixed value. Make sure its float and then pass it to number_format. It will returns you the string.

Convert IEEE754 to hex in PHP

For a project I need to read in information from MQTT. The payload is filled with protobuf information, that needs to be converted.
For a certain value I receive 5.6904566139035E-28 as float. Using http://www.exploringbinary.com/floating-point-converter/ I can convert this when I tick single and raw hexadecimal value, then I receive 12345678, the value I should have (I know what is sent).
But now I need to do that conversion in PHP. I haven't any idea how this could be done. After some reading I figured out it is a Floating Point, but how to convert this like done on that website.
Is there someone that can help me with this!
Thanks a lot!
With the quite cryptic pack and unpack functions, it can be done in a one-liner:
function rawSingleHex($num) {
return strrev(unpack('h*', pack('f', $num))[1]);
}
This "packs" the number as its binary representation, then "unpacks" it in an array with one element: the binary representation in hexadecimal format. This format has the digits in the reversed order, so the function reverses that in the final result.
Call it by passing the floating point number:
echo rawSingleHex(5.6904566139035E-28);
Output:
12345678
Without pack/pack
(this was my original answer, but with the first option being available, this is not the advised way to proceed)
The binary format is explained in Wikipedia's article on the Single-precision floating-point format.
Here is a PHP function that implements the described procedure:
function rawSingleHex($num) {
if ($num == 0) return '00000000';
// set sign bit, and add another, higher one, which will be stripped later
$sign = $num < 0 ? 0x300 : 0x200;
$significant = abs($num);
$exponent = floor(log($significant, 2));
// get 24 most significant binary bits before the comma:
$significant = round($significant / pow(2, $exponent-23));
// exponent has exponent-bias format:
$exponent += 127;
// format: 1 sign bit + 8 exponent bits + 23 significant bits,
// without left-most "1" of significant
$bin = substr(decbin($sign + $exponent), 1) .
substr(decbin($significant), 1);
// assert that result has correct number of bits:
if (strlen($bin) !== 32) {
return "unexpected error";
}
// convert binary representation to hex, with exactly 8 digits
return str_pad(dechex(bindec($bin)), 8, "0", STR_PAD_LEFT);
}
It outputs the same as in the first solution.

PHP rounding error with simple multiplication [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 3 years ago.
PHP seems to round incorrectly when using (int) to cast variables. Why?
$multiplier = 100000000;
$value = 0.01020637;
echo (int)($value*$multiplier);
Output: 1020636. (unexpected output)
$multiplier = 100000000;
$value = 0.01020637;
echo ($value*$multiplier);
Output: 1020637. (Expected correct output)
Edit: it gets even worse...
$multiplier = 100000000;
$value = 0.01020637;
echo $temp = ($value*$multiplier);
echo '<br/>';
echo (int)$temp;
Output:
1020637
1020636
Things can get hairy when you're dealing with floats, floating point math (and problems involved) are well understood, but can crop up when you're not expecting them. As seems to have happened here. You could read up on the rules extensively, or use language provided tools when handling floating point arithmetic.
When you care about the precision involved you should use the bcmul() function. It's an "optional" extension, but if you care about precision it starts being required rather quickly.
Example:
multiplier = 100000000;
$value = 0.01020637;
echo (int)($value*$multiplier);
echo "\n";
echo bcmul($value, $multiplier, 0);
Sample: http://ideone.com/Wt9kKb
PHP (especially in 32 bit builds) has problems with floating point numbers. This is why casting float into int can have unpredictable results. See PHP Integer page for more detail. Basically, you're getting tiny imprecisions in the math and that can cause serious problems when trying to do something like ceil()
If you really need the numbers converted to int I would suggest you round the numbers first
$multiplier = 100000000;
$value = 0.01020637;
$temp = round($value*$multiplier);
echo $temp . '<br/>' . (int)$temp;
This works by truncating off the small floating point errors. While bcmath can also do the truncation, it's not part of PHP core and not a good overall solution. Your best bet is to write a rounding routine yourself that can return the precision you're looking for. In the project I work on, that was what we did. We wrote our own rounding function and it fixes the problems you'll run into. Without knowing the specifics of what you're trying to do it's hard to say if that's what you need but it's how we did it without bcmath.
The problem you're seeing is the following:
When multiplying two numbers like this:
$mulitply = 0.1 * 100;
You are not multiplying exactly 100 with 0.1, but with with 0.09999999998...
And when it comes to (int), it converts numbers like 4.999 to 4, so your result 1020636.999999999 becomes 1020636 when counting with (int).
bcmul allows for higher precision
$test = (int) bcmul('100000000', '0.01020637');
echo $test
returns the correct answer.
To round floats in PHP you should use the round() function. Just casting to an integer does not round the value correctly.
First argument is which float (the result of your calculation in this case) to be rounded, second is optional, and specifies the amount of decimals (aka precision) being returned. There is also a third argument, controlling the mode. These can be PHP_ROUND_HALF_UP, PHP_ROUND_HALF_DOWN, PHP_ROUND_HALF_EVEN or PHP_ROUND_HALF_ODD.
Example from php.net/round:
<?php
echo round(3.4); // 3
echo round(3.6); // 4
echo round(3.6, 0); // 4
echo round(1.95583, 2); // 1.96
// With the third element, "mode"
echo round(9.5, 0, PHP_ROUND_HALF_UP); // 10
echo round(9.5, 0, PHP_ROUND_HALF_DOWN); // 9
echo round(9.5, 0, PHP_ROUND_HALF_EVEN); // 10
echo round(9.5, 0, PHP_ROUND_HALF_ODD); // 9
?>
An example for your code (live example):
<?php
$multiplier = 100000000;
$value = 0.01020637;
echo intval(round($value*$multiplier)); // Returns 1020637
?>

How do I truncate a decimal in PHP?

I know of the PHP function floor() but that doesn't work how I want it to in negative numbers.
This is how floor works
floor( 1234.567); // 1234
floor(-1234.567); // -1235
This is what I WANT
truncate( 1234.567); // 1234
truncate(-1234.567); // -1234
Is there a PHP function that will return -1234?
I know I could do this but I'm hoping for a single built-in function
$num = -1234.567;
echo $num >= 0 ? floor($num) : ceil($num);
Yes intval
intval(1234.567);
intval(-1234.567);
Truncate floats with specific precision:
echo bcdiv(2.56789, 1, 1); // 2.5
echo bcdiv(2.56789, 1, 3); // 2.567
echo bcdiv(-2.56789, 1, 1); // -2.5
echo bcdiv(-2.56789, 1, 3); // -2.567
This method solve the problem with round() function.
Also you can use typecasting (no need to use functions),
(int) 1234.567; // 1234
(int) -1234.567; // -1234
http://php.net/manual/en/language.types.type-juggling.php
You can see the difference between intval and (int) typecasting from here.
another hack is using prefix ~~ :
echo ~~1234.567; // 1234
echo ~~-1234.567; // 1234
it's simpler and faster
Tilde ~ is bitwise NOT operator in PHP and Javascript
Double tilde(~) is a quick way to cast variable as integer, where it is called 'two tildes' to indicate a form of double negation.
It removes everything after the decimal point because the bitwise operators implicitly convert their operands to signed 32-bit integers. This works whether the operands are (floating-point) numbers or strings, and the result is a number
reference:
https://en.wikipedia.org/wiki/Double_tilde
What does ~~ ("double tilde") do in Javascript?
you can use intval(number); but if your number bigger than 2147483648 (and your machine/os is x64) all bigs will be truncated to 2147483648. So you can use
if($number < 0 )
$res = round($number);
else
$res = floor($number);
echo $res;
You can shift the decimal to the desired place, intval, and shift back:
function truncate($number, $precision = 0) {
// warning: precision is limited by the size of the int type
$shift = pow(10, $precision);
return intval($number * $shift)/$shift;
}
Note the warning about size of int -- this is because $number is potentially being multiplied by a large number ($shift) which could make the resulting number too large to be stored as an integer type. Possibly converting to floating point might be better.
You could get fancy with a $base parameter, and sending that to intval(...).
Could (should) also get fancy with error/bounds checking.
An alternative approach would be to treat number as a string, find the decimal point and do a substring at the appropriate place after the decimal based on the desired precision. Relatively speaking, that won't be fast.

Fixing to .2 decimal number [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Default Number of Decimal Places to Output in PHP
basically a bit of a maths problem,
$average_ppm = $total_points_given / $totalvalue;
$average_ppm now equals 2.432608695652174, I don't want to display these numbers, I just need $average_ppm to be 2.43, so to a fixed 2 decimal points. How can I do this??
Thanks for anyones time.
Use sprintf if you want a string output, or round/floor/ceil for a numeric value:
$average_ppm = 2.432608695652174;
echo sprintf("%.2f", $average_ppm); // 2.43
$approx_average_ppm = round($average_ppm, 2);
echo $approx_average_ppm; // 2.43
echo floor($average_ppm, 2); // 2.43 , even if $average_ppm = 2.439
echo ceil($average_ppm, 2); // 2.44
You could either use sprintf, round or floor/ceil depending on how you want the numbers rounded.
Most suited for your need would be round:
$average_ppm = round($total_points_given / $totalvalue,2);
If you want to have ALWAYS 2 numbers after... you can do it with number_format:
number_format(2.43260869565217, 2); // 2.43
When you got a number like: 2.400054846 and you use round you will get 2.4
and if you want it with 2 number behind you can use number_format this will output 2.40
I would use Round:
round($average_ppm, 2);
You could use the bcmath functions if they are available where the third argument is the precision
$average_ppm = bcdiv($total_points_given, $totalvalue, 2);

Categories