Building a Japanese Numeral Reading System on PHP - php

I faced an interview question which i felt was very good. Couldn't achieve the complete answer, however, felt sharing and asking the right method to code it in PHP.
The question goes as :
Given the Japanese numeral reading system, write a program that converts an integer into the equivalent Japanese reading.
Basic numeral readings:
1: ichi
2: ni
3: san
4: yon
5: go
6: roku
7: nana
8: hachi
9: kyuu
10: juu
20: ni-juu
30: san-juu
100: hyaku
1000 : sen
10,000: man
100,000,000: oku
1,000,000,000,000: chou
10,000,000,000,000,000: kei
Exceptions due to voice rounding in Japanese reading:
300: sanbyaku
600: roppyaku
800: happyaku
3000: sanzen
8000: hassen
1,000,000,000,000: itchou
8,000,000,000,000: hatchou
10,000,000,000,000: jutchou (also applies to multiplies of 10,000,000,000,000)
10,000,000,000,000,000: ikkei
60,000,000,000,000,000: rokkei
80,000,000,000,000,000: hakkei
100,000,000,000,000,000: jukkei (also applies to multiplies of 10,000,000,000,000,000)
1,000,000,000,000,000,000: hyakkei (also applies to multiplies of 1,000,000,000,000,000,000)
Starting at 10,000, numbers begin with ichi if no digit would otherwise precede, e.g. 1,000 is sen but 10,000 is ichi-man.
Examples:
11: juu ichi
17: juu nana
151: hyaku go-juu ichi
302: san-byaku ni
469: yon-hyaku roku-juu kyuu
2025 : ni-sen ni-juu go
10,403: ichi-man yon-byaku san
41,892: yon-juu ichi-man happyaku kyuu-juu ni
80,000,000,000,000: hachi-jutchou
The code that i have tried is :
$inputNumber = 2025;
$inputString = (String)$inputNumber;
$numeralReadings = array(
1 => 'ichi',
2 => 'ni',
3 => 'san',
4 => 'yon',
5 => 'go',
6 => 'roku',
7 => 'nana',
8 => 'hachi',
9 => 'kyuu',
10 => 'juu',
20 => 'ni-juu',
30 => 'san-juu',
100 => 'hyaku',
1000 => 'sen',
10000 => 'man',
100000000 => 'oku',
1000000000000 => 'chou',
10000000000000000 => 'kei'
);
$numeralExceptions = array(
300 => 'sanbyaku',
600 => 'roppyaku',
800 => 'happyaku',
3000 => 'sanzen',
8000 => 'hassen',
1000000000000 => 'itchou',
8000000000000 => 'hatchou',
10000000000000 => 'jutchou',
10000000000000000 => 'ikkei',
60000000000000000 => 'rokkei',
80000000000000000 => 'hakkei',
100000000000000000 => 'jukkei',
1000000000000000000 => 'hyakkei'
);
if ($inputString > 10000) {
$inp1 = floor($inputString / 1000);
$inp = $inputString - ($inp1 * 1000);
if($inp !== 0) {
read($inp1, $numeralReadings, $numeralExceptions, false);
read($inp, $numeralReadings, $numeralExceptions);
} else {
read($inputString, $numeralReadings, $numeralExceptions);
}
} else {
read($inputString, $numeralReadings, $numeralExceptions);
}
function read($inputStr, $numeralReadings, $numeralExceptions, $parse1 = true)
{
$splitString = str_split($inputStr);
$returnString = '';
$appendIchi = false;
$firstNumber = null;
foreach ($splitString as $key => $number) {
if ($firstNumber == null) {
$firstNumber = $number;
}
if ($number !== 0) {
$int = 1;
$a = count($splitString) - 1 - $key;
for ($i = 0; $i < $a; $i++) {
$int = $int * 10;
}
$tempNumber = (int)$number * $int;
if (isset($numeralExceptions[$tempNumber])) {
$returnString .= $numeralExceptions[$tempNumber] . ' ';
continue;
}
if (isset($numeralReadings[$tempNumber])) {
if ($parse1 == false && $tempNumber == 1) {
continue;
}
$returnString .= $numeralReadings[$tempNumber] . ' ';
continue;
}
if (isset($numeralReadings[(int)$number])) {
if ($parse1 == false && $tempNumber == 1) {
continue;
}
$returnString .= $numeralReadings[(int)$number];
if ($int !== 1) {
$returnString .= '-' . $numeralReadings[$int];
}
$returnString .= ' ';
}
}
}
echo $returnString;
}
here is a fiddle that shows the code in running. You might want to try it online. Link
With the code above, i was able to achieve all the examples stated above other than the last 2.
Anyone who can solve this in a better way?

I guess you can simply use array_key_exists() here
function read($inputStr, $numeralReadings, $numeralExceptions)
{
if(array_key_exists($inputStr, $numeralReadings))
{
return $numeralReadings[$inputStr];
}
else if(array_key_exists($inputStr, $numeralExceptions))
{
return $numeralExceptions[$inputStr];
}
else
{
return "not found";
}
}

Related

Low speed of saving to the MySQL (PHP - Yii2)

I am trying to import data into MySQL from a JSON file.
public function importProductFile($file, $return = true)
{
$products = json_decode($file);
$dubTableName = Product::tableName() . "_dub";
$start = time();
if ($this->db->createDuplicateTable(Product::tableName(), $dubTableName)) {
$i = 0;
foreach ($products as $product) {
$i++;
$item = new Product_dub();
$item->id_1c_product = $product->id;
$category = Category_dub::findOne(['id_1c_category' => $product->category_id]);
if (!$category) {
Answer::failure("В этом товаре отсутствует категория или такой категории не существует: " . $product->title);
}
$item->category_id = $category->id;
$item->title = $product->title;
$brand = Brands_dub::findOne(['id_1c_brand' => $product->brand_id]);
if (!$brand) {
Answer::failure("В этом товаре отсутствует бренд/изготовитель: " . $product->title);
}
$item->brand_id = $brand->id;
// $item->shortdesc = $product->shortdesc;
$item->content1 = $product->content1;
$item->content2 = $product->content2;
$item->content3 = $product->content3;
$item->link_order = $product->link_order;
$item->img = $product->img;
$item->in_stock = $product->in_stock ? 1 : 0;
$item->is_popular = $product->is_popular ? 1 : 0;
if (!$item->save()) {
Answer::failure("Не удалось импортировать: Проверьте данные в " . $product->title);
}
if ($i == 200) {
break;
}
}
}
$finish = time();
$res = $finish - $start . "sec. ";
if ($return) {
echo $res;
Answer::success();
}
}
There are about 1100 objects in my JSON file. It takes 7 seconds to add 100 rows to the database. Adding 200 lines - 15 seconds. 300 = 33 sec, 400 = 58 sec. Why does it slow down over time and how to speed up this process?
I do everything on the local OpenServer server.
PHP 7.2 version, Xeon 2620v3 processor, 16 GB DDR4, HDD.
UPD 1.
"Can you try not importing and just determine the speed of reading" - I comment $item->save() and get 1-2 sec for all of JSON files. "In each iteration of your cycle you are running 2 DB queries to load category and brand." - I tried to delete these lines for test - but the result was 1-2 seconds faster than with 2 DB queries.
UPD 2.
I changed save() to insert() - the speed has increased. Now all JSON (1107 lines) is imported in 40 seconds.
Are there faster ways to load ready-made data from JSON into the database?
What if there are 100 thousand lines or a million? Is it normal practice to wait a few hours?
public function importProductFile($file, $return = true)
{
$products = json_decode($file);
$dubTableName = Product::tableName() . "_dub";
$start = time();
if ($this->db->createDuplicateTable(Product::tableName(), $dubTableName)) {
$start = time();
$categoryMap = Category_dub::find()->select(['id', 'id_1c_category'])->indexBy('id_1c_category')->column();
$brandMap = Brands_dub::find()->select(['id', 'id_1c_brand'])->indexBy('id_1c_brand')->column();
foreach ($products as $product) {
Yii::$app->db->createCommand()->insert('product_dub', [
'id_1c_product' => $product->id,
'category_id' => $categoryMap[$product->category_id] ?? '0',
'title' => $product->title,
'brand_id' => $brandMap[$product->brand_id] ?? 'No brand',
'content1' => $product->content1,
'content2' => $product->content2,
'content3' => $product->content3,
'link_order' => $product->link_order,
'img' => $product->img ?? 'no-image.png',
'in_stock' => $product->in_stock ? 1 : 0,
'is_popular' => $product->is_popular ? 1 : 0,
])->execute();
}
}
}
$finish = time();
$res = $finish - $start . "sec. ";
if ($return) {
echo $res;
Answer::success();
}
}
I changed save() to insert() - the speed has increased. Now all JSON (1107 lines) is imported in 40 seconds.
Are there faster ways to load ready-made data from JSON into the database?
What if there are 100 thousand lines or a million? Is it normal practice to wait a few hours?
public function importProductFile($file, $return = true)
{
$products = json_decode($file);
$dubTableName = Product::tableName() . "_dub";
$start = time();
if ($this->db->createDuplicateTable(Product::tableName(), $dubTableName)) {
$start = time();
$categoryMap = Category_dub::find()->select(['id', 'id_1c_category'])->indexBy('id_1c_category')->column();
$brandMap = Brands_dub::find()->select(['id', 'id_1c_brand'])->indexBy('id_1c_brand')->column();
foreach ($products as $product) {
Yii::$app->db->createCommand()->insert('product_dub', [
'id_1c_product' => $product->id,
'category_id' => $categoryMap[$product->category_id] ?? '0',
'title' => $product->title,
'brand_id' => $brandMap[$product->brand_id] ?? 'No brand',
'content1' => $product->content1,
'content2' => $product->content2,
'content3' => $product->content3,
'link_order' => $product->link_order,
'img' => $product->img ?? 'no-image.png',
'in_stock' => $product->in_stock ? 1 : 0,
'is_popular' => $product->is_popular ? 1 : 0,
])->execute();
}
}
}
$finish = time();
$res = $finish - $start . "sec. ";
if ($return) {
echo $res;
Answer::success();
}
}
You can use the bulk insert as mentioned in this answer and Yii2 docs. Using this bulk insert, you need to remember that the event will not be triggered.
Yii::$app->db->createCommand()->batchInsert('product_dub', array_keys(reset($products)), $products)->execute();

How to determine if the given postal code is between (K2T - K4P) [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
How to determine if the given postal code is between (K2T - K4P). This is my code. Looking to get the right results from get_rate_code();
class Helpers
{
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static function getBetweenLetters($first_letter = "A", $last_letter = "Z") {
$first_letter_position = strpos(SELF::ALPHABET, $first_letter);
$last_letter_position = strpos(SELF::ALPHABET, $last_letter) +1;
return substr(SELF::ALPHABET, $first_letter_position, ($last_letter_position-$first_letter_position));
}
public static function getBetweenNumbers($first_number = 0,
$last_number = 9,
$target_number = 0)
{
$string = "";
for($x = $first_number; $x < $last_number+1; $x++) {
if($x == $target_number)
return true;
}
return false;
}
}
class DeliverySBR
{
// Properties
public $prov;
public $destination;
public $rate_code;
function __construct($destination = "") {
$this->destination = $destination;
}
function set_destination($destination) {
$this->destination = $destination;
}
function get_destination() {
return $this->destination;
}
function get_rate_code() {
switch ( substr($this->destination, 0, 1) ) {
case "K":
if( substr($this->destination, 1, 1) == 0 && is_int(strpos(Helpers::getBetweenLetters("A", "M"), substr($this->destination,2,1) )) )
return "D2 (K0A- K0M)"; // K0A- K0M
else if( Helpers::getBetweenNumbers(1,2,substr($this->destination, 1, 1) ) && is_int(strpos(Helpers::getBetweenLetters("A", "R"), substr($this->destination,2,1) ) ) )
return "B2 (K1A- K2R)"; // K1A- K2R;
else if (substr($this->destination,1,2) == "2S")
return "D2 (K2S)"; // K2S
else if (Helpers::getBetweenNumbers(2,4,substr($this->destination, 1, 1) ) && is_int(strpos(Helpers::getBetweenLetters("P", "T"), substr($this->destination,2,1) ) ) )
return "B2 (K2T - K4P)"; // K2T - K4P
else if (Helpers::getBetweenNumbers(4,7,substr($this->destination, 1, 1) ) && is_int(strpos(Helpers::getBetweenLetters("H", "R"), substr($this->destination,2,1) ) ) )
return "D2 (K4R - K7H)"; // K4R - K7H
//else if()
// return ""; // K7K- K7P
break;
default:
return false;
}
}
$delivery1 = new DeliverySBR("KOMB73");
d($delivery1->get_rate_code(), $delivery1->get_destination()); // Works good result: "D2 (K0A- K0M)"
$delivery2 = new DeliverySBR("K2S173");
d($delivery2->get_rate_code(), $delivery2->get_destination()); // Works good result: D2 (K2S)
$delivery3 = new DeliverySBR("K2TL47");
d($delivery3->get_rate_code(), $delivery3->get_destination()); // Works good result: B2 (K2T - K4P)
$delivery4 = new DeliverySBR("K4R1L3");
d($delivery4->get_rate_code(), $delivery4->get_destination()); // BAD gets B2 (K2T - K4P) instead of D2 (K4R - K7H)
$delivery5 = new DeliverySBR("K2Z1L3");
d($delivery5->get_rate_code(), $delivery5->get_destination()); // BAD this returns null. expecting to return B2 (K2T - K4P)
}
its giving me wrong results:
d($delivery4->get_rate_code(), $delivery4->get_destination()); // BAD gets B2 (K2T - K4P) instead of D2 (K4R - K7H)
and
d($delivery5->get_rate_code(), $delivery5->get_destination()); // BAD this returns null. expecting to return B2 (K2T - K4P)
I believe you're overengineering this, since these codes are directly comparable. The following should suffice:
function get_rate_code(string $postalCode): string
{
$rateCodeRanges = [
'D2 (K0A- K0M)' => ['start' => 'K0A', 'end' => 'K0M'],
'B2 (K1A- K2R)' => ['start' => 'K1A', 'end' => 'K2R'],
'D2 (K2S)' => ['start' => 'K2S', 'end' => 'K2S'],
'B2 (K2T - K4P)' => ['start' => 'K2T', 'end' => 'K4P'],
'D2 (K4R - K7H)' => ['start' => 'K4R', 'end' => 'K7H'],
];
foreach ($rateCodeRanges as $rateCode => $rateCodeRange) {
if ($postalCode >= $rateCodeRange['start'] && $postalCode <= $rateCodeRange['end']) {
return $rateCode;
}
}
return '';
}
Calling the following:
var_dump(get_rate_code('K0M'));
var_dump(get_rate_code('K2S'));
var_dump(get_rate_code('K2T'));
var_dump(get_rate_code('K4R'));
var_dump(get_rate_code('K2Z'));
will give you:
'D2 (K0A- K0M)'
'D2 (K2S)'
'B2 (K2T - K4P)'
'D2 (K4R - K7H)'
'B2 (K2T - K4P)'
I've made the function return an empty string on no match to keep it down to a single return type, but you can adapt this to whatever fits your needs.
You can use the following pattern and duplicate for the other code ranges:
if(preg_match('/K(2[T-Z]|3[A-Z]|4[A-P])/', $this->destination)) {
return "B2 (K2T - K4P)";
}
Match K and
2 and T-Z OR |
3 and A-Z OR |
4 and A-P

PHP Convert any kg amount to a readable metric unit

I am trying to create a mathematical function (in PHP) that will take a given number of Kg, and convert them to a readable form or better yet, return the unit best suited for that amount. The input will always be kg. Preferably log.
For example:
5 kg = (5) kg
0.5 kg = (500) gm
1000 kg = (1) tonne
0.001 kg = (1) gm
0.0001 kg = (100) mg
I know there is a way to do it using log or log10 functions but I cannot figure it out.
How could it be done?
I think something like this should work, but I'm sure there are people who can make a much better solution.
function outputWeight($kg)
{
$power = floor(log($kg, 10));
switch($power) {
case 5 :
case 4 :
case 3 : $unit = 'ton';
$power = 3;
break;
case 2 :
case 1 :
case 0 : $unit = 'kilogram';
$power = 0;
break;
case -1 :
case -2 :
case -3 : $unit = 'gram';
$power = -3;
break;
case -4 :
case -5 :
case -6 : $unit = 'milligram';
$power = -6;
break;
default : return 'out of range';
}
return ($kg / pow(10, $power)) . ' ' . $unit;
}
echo outputWeight(0.015) . '<br>';
echo outputWeight(0.15) . '<br>';
echo outputWeight(1.5) . '<br>';
echo outputWeight(15) . '<br>';
echo outputWeight(150) . '<br>';
The idea is that you can easily extend the range. This will output
15 gram
150 gram
1.5 kilogram
15 kilogram
150 kilogram
I did not thoroughly test this code!
After playing with it for a while, here is what I came up with
function readableMetric($kg)
{
$amt = $kg * pow(1000, 3);
$s = array('mcg', 'mg', 'gm', 'kg','tonne');
$e = floor(log10($amt)/log10(1000));
return [
"amount" => $amt/pow(1000, $e),
"unit" => $s[$e]
];
}
The following function internally uses an array with the assignments unit => conversion-factor. This array can easily be expanded or modified to meet your own requirements. A fixed limit of 1000 is used in the function. This means that the output value is always less than 1000, with the exception of tons. With an additional argument which is preset to 2, the number of maximum decimal places can be changed.
function scaleKg(float $kg, int $decimalPlaces = 2) : string {
$scale = [
'micrograms' => 1.E9,
'milligram' => 1.E6,
'gram' => 1.E3,
'kilogram' => 1,
'ton' => 1.E-3,
];
foreach($scale as $unit => $factor){
$mass = $kg * $factor;
if($mass < 1000) {
return round($mass,$decimalPlaces).' '.$unit;
}
}
return round($mass,$decimalPlaces).' '.$unit;
}
Some examples:
$kg = 1.212345;
echo scaleKg($kg); //1.21 kilogram
$kg = 455;
echo scaleKg($kg); //455 kilogram
$kg = 0.0456;
echo scaleKg($kg); //45.6 gram
$kg = 23456;
echo scaleKg($kg); //23.46 ton
$kg = 23489000;
echo scaleKg($kg); //23489 ton
$kg = 167E-6;
echo scaleKg($kg); //167 milligram
I find myself agreeing with Kiko that the shortest code isn't always the best or most readable.
Bearing that in mind bringing log into it seems like unnecessary complication. So I suggest a condensed version of my original code:
function readableMetric($mass)
{
$units = [
-3 => "tonne",
0 => "kg",
3 => "g",
6 => "mg",
];
foreach ($units as $x => $unit) {
if ( ($newMass = $mass * 10 ** $x) >= 1 ) {
return "{$newMass} {$unit}";
}
}
}
This is more extensible (e.g. you could easily add centigram)
The math is intuitive to most (powers of 10)
If you really want to golf it you can shrink it down to a one liner:
function readableMetric($m)
{
foreach([-3=>"tonne",0=>"kg",3=>"g",6 =>"mg"] as $x=>$g)if(($n=$m*10**$x)>=1)return"$n $g";
}
Original Answer
You could just do it with a series of if / else statements?
$values = [
5, 0.5, 1000, 0.001, 0.0001
];
function convertmass($mass) : string
{
if ($mass >= 1000) {
return ($mass / 1000) . " ton";
}
elseif ($mass < 0.001) {
return ($mass * 1000000) . " mg";
}
elseif ($mass < 1) {
return ($mass * 1000 ) . " g";
}
else {
return $mass . " kg";
}
}
foreach ($values as $mass) {
echo convertmass($mass), PHP_EOL;
}
Output:
5 kg
500 g
1 ton
1 g
100 mg
If you want slightly easier/more readable/more user friendly updating to add new measurements in then you can do something like this instead:
$values = [
5, 0.5, 1000, 0.001, 0.0001
];
function convertmass($mass) : string
{
$massLookup = [
[ "unit" => "kg", "min" => 1, "max" => 1000, "multiple" => 1],
[ "unit" => "tonne", "min" => 1000, "max" => 1000000, "multiple" => 0.001],
[ "unit" => "g", "min" => 0.001, "max" => 1, "multiple" => 1000],
[ "unit" => "mg", "min" => 0.000001, "max" => 0.001, "multiple" => 1000000],
];
foreach ($massLookup as $unit) {
if ($mass >= $unit["min"] && $mass < $unit["max"]) {
return ($mass * $unit["multiple"]) . " {$unit["unit"]}";
}
}
return "Measurement {$mass} kg is out of range";
}
foreach ($values as $mass) {
echo convertmass($mass), PHP_EOL;
}

Big Associative Array into Smaller Associative Arrays using Array_filter

I'm working on calculating the pass ratings of various football players. I have a .txt file that I'm pulling the info from.
while($line = fgetcsv($fp, 255, ',')){
$rate = round(calcPR($line),2);
$rating[$line[0]] = $rate;
} return $rating;
That was the portion of my function where I'm using a $fp to read from my .txt file and here is where I'm trying to display the data, but before I attempt to display my data I try to split the data into pass ratings that are great,good,mediocre, and poor.
For a great rating, they need to be above 95 so I have:
$grtRating = array_filter($rating,function(){
return $rating > 95;
});
The rest of my code for good, mediocre, and poor looks just about the same, but with different criteria. How can I get this $grtRating array to store only the scores that are above 95?
Currently when I run my program it basically ignores the operators and displays all of the ratings no matter how low.
UPDATE:
Output of $poorRating array: (This is everything < 86)
Array ( [Cody Kessler] => 85.91 [Kirk Cousins] => 82.34 [Jacoby Brissett] => 81.68 [Ryan Tannehill] => 81.2 [Tyrod Taylor] => 79.29 [Ben Roethlisberger] => 77.49 [Shaun Hill] => 77.32 [Carson Palmer] => 74.79 [Jameis Winston] => 73.93 [Marcus Mariota] => 73.06 [Joe Flacco] => 71.77 [Cam Newton] => 70.32 [Josh McCown] => 70.25 [Trevone Boykin] => 69.2 [Jay Cutler] => 68.46 [Blake Bortles] => 67.13 [Brock Osweiler] => 66.1 [Blaine Gabbert] => 63.35 [Case Keenum] => 60.63 [Ryan Fitzpatrick] => 48.93 [Robert Griffin III] => 48.54 [Drew Stanton] => 34.36 [Kellen Clemens] => 2.07 )
I think the problem might be in how I output my code then. I have my links in an unordered list.
Here is an example:
<a href='pr.php?action=all'>All Ratings</a>
I have a pr.php?action=great',pr.php?action=good', pr.php?action=mediocre', andpr.php?action='poor'.
Here is how I output everything, is the problem how I do that?
$mode = 'all';
if ($_GET['action'] == 'great') $mode = 'great';
if ($_GET['action'] == 'good') $mode = 'good';
if ($_GET['action'] == 'mediocre') $mode = 'mediocre';
if ($_GET['action'] == 'poor') $mode = 'poor';
if($mode == 'great'){
foreach($greatRating as $name=>$pr){
echo "<tr><td>{$name}</td><td>{$pr}</td></tr>\n";
}
}
if($mode == 'good'){
foreach($goodRating as $name=>$pr){
echo "<tr><td>{$name}</td><td>{$pr}</td></tr>\n";
}
}
if($mode == 'mediocre'){
foreach($mediocreRating as $name=>$pr){
echo "<tr><td>{$name}</td><td>{$pr}</td></tr>\n";
}
}
if($mode == 'poor'){
foreach($poorRating as $name=>$pr){
echo "<tr><td>{$name}</td><td>{$pr}</td></tr>\n";
}
}
if($mode = 'all'){
foreach($rating as $name=>$pr){
echo "<tr><td>{$name}</td><td>{$pr}</td></tr>\n";
}
}
echo "</table></div>\n";
}
Would you please help me figure out where my error is?
Replace
$grtRating = array_filter($rating,function(){
return $rating > 95;
});
with
$grtRating = array_filter($rating, function($val) {
return $val > 95;
});
In the first one, you did not include the callback argument that contains the value passed from each iteration of the array, which will be the one used in your evaluation.

Simple addition and subtraction not working correctly

I've inherited a project written in PHP which has a double entry style cashbook, which requires that the amount going in is equal to the amount going out.
When validating figures entered via a form (to work out if it equals zero), it runs the code below which works fine until there is a decimal number, which seems to make it randomly fall over and return an incorrect value.
In the example below, it returns a really tiny number from 53.87 - 53.87, which should be zero.This has been stripped back to eliminate other things from causing problems, so I've removed a lot of input validation etc.
<?php $inputs = array(
array(
"in" => '',
"out" => '249.6',
),
array(
"in" => '',
"out" => '396',
),
array(
"in" => '554.4',
"out" => ''
),
array(
"in" => '145.07',
"out" => ''
),
array(
"in" => '',
"out" => '53.87',
),
);
$fTotal = 0;
echo "Start at 0: ";
foreach($inputs as $key=>$sRef) {
$itemValid = true;
$aItem = array();
$amountIn = $inputs[$key]['in'];
$amountOut = $inputs[$key]['out'];
if ($itemValid) {
echo "'".$fTotal."'";
$is_in = 0;
if ($amountIn > 0.0) {
$is_in = 1;
echo "+";
$amount = $amountIn;
$fTotal = $fTotal + $amountIn;
} else {
$is_in = 0;
echo "-";
$amount = $amountOut;
$fTotal = $fTotal - $amountOut;
}
echo "'".$amount."'=";
echo "'".$fTotal."' | ";
$aItem["is_in"] = $is_in;
$aItem["amount"] = $amount;
$aItems[] = $aItem;
}
}
You can run this on a sandbox here.
Here is the expected output:
Start at 0: '0'-'249.6'='-249.6' | '-249.6'-'396'='-645.6' | '-645.6'+'554.4'='-91.2' | '-91.2'+'145.07'='53.87' | '53.87'-'53.87'='0' |
Here is the actual output:
Start at 0: '0'-'249.6'='-249.6' | '-249.6'-'396'='-645.6' | '-645.6'+'554.4'='-91.2' | '-91.2'+'145.07'='53.87' | '53.87'-'53.87'='-4.9737991503207E-14' |
What is wrong here?
Update
Following help below, here's the working code for anyone who stumbles on this in the future.
This is happening because of floating point (decimal) maths where computers like binary maths. Sometimes decimal numbers don't have a nice representation in binary so these tiny differences happen.
If you can state that every number can be rounded to 2 decimal points, and based on your limited dataset, wrapping your 'sums' in number_format($fTotal + $amountIn , 2) etc can sort this out for you.
Alternatively for a "more accurate", just wrap the final one eg. echo "'".number_format($fTotal, 2)."' | "; (or 0 or whatever)
Your numbers are being interpreted as strings.
Try this:
<?php
$inputs = array(
array(
"in" => 0,
"out" => 249.6,
),
array(
"in" => 0,
"out" => 396,
),
array(
"in" => 554.4,
"out" => 0
),
array(
"in" => 145.07,
"out" => 0
),
array(
"in" => 0,
"out" => 53.87,
),
);
$fTotal = 0;
echo "Start at 0: ";
foreach($inputs as $key => $sRef) {
$itemValid = true;
$amountIn = $sRef['in'];
$amountOut = $sRef['out'];
if ($itemValid) {
echo $fTotal;
$is_in = 0;
if ($amountIn > 0.0) {
$is_in = 1;
echo "+";
$amount = floatval($amountIn);
$fTotal = floatval($fTotal) + floatval($amountIn);
} else {
$is_in = 0;
echo "-";
$amount = floatval($amountOut);
$fTotal = floatval($fTotal) - floatval($amountIn);
}
echo $amount;
echo ' = ' . $fTotal . " | ";
}
}
The bcsub() function in PHP is an inbuilt function and is used to subtract one arbitrary precision number from another.
Syntax:
string bcsub ( $num_str1, $num_str2, $scaleVal)
Example :
<?php $num_str1 = "8"; $num_str2 = "3"; $res = bcsub($num_str1, $num_str2) echo $res; ?>
Result:
5

Categories