Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dfelton/b472ef1b26dec2a9878e1652eb313389 to your computer and use it in GitHub Desktop.

Select an option

Save dfelton/b472ef1b26dec2a9878e1652eb313389 to your computer and use it in GitHub Desktop.
PHP - Prove max $scale param necessary for bcmul() operations

Can be used to assert the max scale necessary for bcmul() operations between two string representations of floating point values.

Theory is that the max scale necessary is a simple calculation of maxScale = numDigitsAfterDecimalOfLeftOperand + numDigitsAfterDecimalOfRightOperand

Logic could probably be simplified by converting left and right operand values to whole numbers, performing calculation, and converting back to floating point number. Alongside this, perform bcmul() operation with "expected max scale", trimming trailing zeros, and asserting results of the two approaches are equal. (this would eliminate the need for the $maxSupport approach below)

Asserts this truth by iterating through all possible values of two floating point numbers for specified input. For exammple if $leftOperandStart given is 0.1 and $rightOperandStart given is 0.1, 100 operations are performed, incrementing each operand by 0.1. Continuing until we have multiplied '0.1' * '0.1' to '1.0' * '1.0'. If $leftOperandStart given is '0.01' and $rightOperandStart given is '0.01', 10,000 operations are performed until same end comparison of '1.0' * '1.0' has been performed.

Demonstration purposes only. Not meant to serve as production code in any application.

<?php
function calculateMaxScale(string $leftOperandStart = '0.1', string $rightOperandStart = '0.1') : void
{
if (\preg_replace('/[^0-1\.]/', '', $leftOperandStart) !== $leftOperandStart) {
throw new \Exception('left operand must contain "0", ".", and "1"');
} elseif (\preg_replace('/[^0-1\.]/', '', $rightOperandStart) !== $rightOperandStart) {
throw new \Exception('right operand must contain "0", ".", and "1"');
} elseif ($leftOperandStart[0] !== '0' || $leftOperandStart[1] !== '.') {
throw new \Exception(\sprintf('left operand must start with "0.", "%s" supplied', $leftOperandStart));
} elseif ($rightOperandStart[0] !== '0' || $rightOperandStart[1] !== '.') {
throw new \Exception(\sprintf('right operand must start with "0.", "%s" supplied', $rightOperandStart));
} elseif (\substr_count($leftOperandStart, '.') !== 1) {
throw new \Exception('left operand must only one and only one decimal point character');
} elseif (\substr_count($rightOperandStart, '.') !== 1) {
throw new \Exception('right operand must only one and only one decimal point character');
}
$leftOperand = $leftOperandIncrement = $leftOperandStart;
$rightOperand = $rightOperandIncrement = $rightOperandStart;
$leftOperandWhole = \explode('.', $leftOperand);
$leftOperandWhole = $leftOperandWhole[1];
$leftOperandIterations = \str_pad('1', \strlen($leftOperandWhole) + 1, '0', STR_PAD_RIGHT);
$rightOperandWhole = \explode('.', $rightOperand);
$rightOperandWhole = $rightOperandWhole[1];
$rightOperandIterations = \str_pad('1', \strlen($rightOperandWhole) + 1, '0', STR_PAD_RIGHT);
$iterations = 0;
$maxScale = 0;
$expected = \strlen($leftOperandWhole) + \strlen($rightOperandWhole);
// We would have to have a calculation that yields 1000 zeros beyond our
// "expected" max scale, THEN non-zero values afterwards, for this
// method to yield a false result.
$maxSupport = $expected + 1000;
do {
$iterations++;
$innerIterations = 0;
do {
$innerIterations++;
$value = \bcmul($leftOperand, $rightOperand, $maxSupport);
$value = \rtrim($value, '0');
$scale = \explode('.', $value);
$scale = $scale[1];
$scale = \strlen($scale);
$value = \rtrim($value, '.');
if ($scale > $maxScale) {
$maxScale = $scale;
}
echo \sprintf(
"\tExpected: %s\tMax Scale: %s\tLeft Operand: %s\tRight Operand: %s\tScale: %s\tValue: %s\n",
$expected, $maxScale, $leftOperand, $rightOperand, $scale, $value
);
if ($expected < $maxScale) {
throw new \Exception (\sprintf(
'ERROR! Theory proven wrong. Expected max scale "%s". Actual max scale "%s" for last operation.',
$expected,
$maxScale
));
}
$rightOperand = \bcadd($rightOperand, $rightOperandIncrement, \strlen($rightOperandWhole));
} while ($innerIterations < $rightOperandIterations);
$leftOperand = \bcadd($leftOperand, $leftOperandIncrement, \strlen($leftOperandWhole));
$rightOperand = $rightOperandStart;
} while ($iterations < $leftOperandIterations);
}
$exampleTests = [
['0.1', '0.1'],
['0.1', '0.01'],
['0.1', '0.001'],
['0.1', '0.0001'],
['0.1', '0.00001'],
['0.1', '0.000001'],
['0.1', '0.0000001'],
['0.1', '0.00000001'],
['0.1', '0.000000001'],
['0.1', '0.0000000001'],
['0.01', '0.1'],
['0.01', '0.01'],
['0.01', '0.001'],
['0.01', '0.0001'],
['0.01', '0.00001'],
['0.01', '0.000001'],
['0.01', '0.0000001'],
['0.01', '0.00000001'],
['0.01', '0.000000001'],
['0.01', '0.0000000001'],
['0.001', '0.1'],
['0.001', '0.01'],
['0.001', '0.001'],
['0.001', '0.0001'],
['0.001', '0.00001'],
['0.001', '0.000001'],
['0.001', '0.0000001'],
['0.001', '0.00000001'],
['0.001', '0.000000001'],
['0.001', '0.0000000001'],
['0.0001', '0.1'],
['0.0001', '0.01'],
['0.0001', '0.001'],
['0.0001', '0.0001'],
['0.0001', '0.00001'],
['0.0001', '0.000001'],
['0.0001', '0.0000001'],
['0.0001', '0.00000001'],
['0.0001', '0.000000001'],
['0.0001', '0.0000000001'],
['0.00001', '0.1'],
['0.00001', '0.01'],
['0.00001', '0.001'],
['0.00001', '0.0001'],
['0.00001', '0.00001'],
['0.00001', '0.000001'],
['0.00001', '0.0000001'],
['0.00001', '0.00000001'],
['0.00001', '0.000000001'],
['0.00001', '0.0000000001'],
['0.000001', '0.1'],
['0.000001', '0.01'],
['0.000001', '0.001'],
['0.000001', '0.0001'],
['0.000001', '0.00001'],
['0.000001', '0.000001'],
['0.000001', '0.0000001'],
['0.000001', '0.00000001'],
['0.000001', '0.000000001'],
['0.000001', '0.0000000001'],
['0.0000001', '0.1'],
['0.0000001', '0.01'],
['0.0000001', '0.001'],
['0.0000001', '0.0001'],
['0.0000001', '0.00001'],
['0.0000001', '0.000001'],
['0.0000001', '0.0000001'],
['0.0000001', '0.00000001'],
['0.0000001', '0.000000001'],
['0.0000001', '0.0000000001'],
['0.00000001', '0.1'],
['0.00000001', '0.01'],
['0.00000001', '0.001'],
['0.00000001', '0.0001'],
['0.00000001', '0.00001'],
['0.00000001', '0.000001'],
['0.00000001', '0.0000001'],
['0.00000001', '0.00000001'],
['0.00000001', '0.000000001'],
['0.00000001', '0.0000000001'],
['0.000000001', '0.1'],
['0.000000001', '0.01'],
['0.000000001', '0.001'],
['0.000000001', '0.0001'],
['0.000000001', '0.00001'],
['0.000000001', '0.000001'],
['0.000000001', '0.0000001'],
['0.000000001', '0.00000001'],
['0.000000001', '0.000000001'],
['0.000000001', '0.0000000001'],
['0.0000000001', '0.1'],
['0.0000000001', '0.01'],
['0.0000000001', '0.001'],
['0.0000000001', '0.0001'],
['0.0000000001', '0.00001'],
['0.0000000001', '0.000001'],
['0.0000000001', '0.0000001'],
['0.0000000001', '0.00000001'],
['0.0000000001', '0.000000001'],
['0.0000000001', '0.0000000001'],
];
foreach ($exampleTests as $test) {
\calculateMaxScale($test[0], $test[1]);
}
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 0.1 Scale: 2 Value: 0.01
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 0.2 Scale: 2 Value: 0.02
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 0.3 Scale: 2 Value: 0.03
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 0.4 Scale: 2 Value: 0.04
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 0.5 Scale: 2 Value: 0.05
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 0.6 Scale: 2 Value: 0.06
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 0.7 Scale: 2 Value: 0.07
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 0.8 Scale: 2 Value: 0.08
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 0.9 Scale: 2 Value: 0.09
Expected: 2 Max Scale: 2 Left Operand: 0.1 Right Operand: 1.0 Scale: 1 Value: 0.1
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 0.1 Scale: 2 Value: 0.02
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 0.2 Scale: 2 Value: 0.04
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 0.3 Scale: 2 Value: 0.06
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 0.4 Scale: 2 Value: 0.08
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 0.5 Scale: 1 Value: 0.1
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 0.6 Scale: 2 Value: 0.12
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 0.7 Scale: 2 Value: 0.14
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 0.8 Scale: 2 Value: 0.16
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 0.9 Scale: 2 Value: 0.18
Expected: 2 Max Scale: 2 Left Operand: 0.2 Right Operand: 1.0 Scale: 1 Value: 0.2
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 0.1 Scale: 2 Value: 0.03
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 0.2 Scale: 2 Value: 0.06
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 0.3 Scale: 2 Value: 0.09
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 0.4 Scale: 2 Value: 0.12
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 0.5 Scale: 2 Value: 0.15
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 0.6 Scale: 2 Value: 0.18
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 0.7 Scale: 2 Value: 0.21
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 0.8 Scale: 2 Value: 0.24
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 0.9 Scale: 2 Value: 0.27
Expected: 2 Max Scale: 2 Left Operand: 0.3 Right Operand: 1.0 Scale: 1 Value: 0.3
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 0.1 Scale: 2 Value: 0.04
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 0.2 Scale: 2 Value: 0.08
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 0.3 Scale: 2 Value: 0.12
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 0.4 Scale: 2 Value: 0.16
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 0.5 Scale: 1 Value: 0.2
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 0.6 Scale: 2 Value: 0.24
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 0.7 Scale: 2 Value: 0.28
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 0.8 Scale: 2 Value: 0.32
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 0.9 Scale: 2 Value: 0.36
Expected: 2 Max Scale: 2 Left Operand: 0.4 Right Operand: 1.0 Scale: 1 Value: 0.4
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 0.1 Scale: 2 Value: 0.05
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 0.2 Scale: 1 Value: 0.1
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 0.3 Scale: 2 Value: 0.15
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 0.4 Scale: 1 Value: 0.2
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 0.5 Scale: 2 Value: 0.25
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 0.6 Scale: 1 Value: 0.3
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 0.7 Scale: 2 Value: 0.35
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 0.8 Scale: 1 Value: 0.4
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 0.9 Scale: 2 Value: 0.45
Expected: 2 Max Scale: 2 Left Operand: 0.5 Right Operand: 1.0 Scale: 1 Value: 0.5
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 0.1 Scale: 2 Value: 0.06
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 0.2 Scale: 2 Value: 0.12
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 0.3 Scale: 2 Value: 0.18
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 0.4 Scale: 2 Value: 0.24
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 0.5 Scale: 1 Value: 0.3
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 0.6 Scale: 2 Value: 0.36
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 0.7 Scale: 2 Value: 0.42
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 0.8 Scale: 2 Value: 0.48
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 0.9 Scale: 2 Value: 0.54
Expected: 2 Max Scale: 2 Left Operand: 0.6 Right Operand: 1.0 Scale: 1 Value: 0.6
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 0.1 Scale: 2 Value: 0.07
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 0.2 Scale: 2 Value: 0.14
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 0.3 Scale: 2 Value: 0.21
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 0.4 Scale: 2 Value: 0.28
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 0.5 Scale: 2 Value: 0.35
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 0.6 Scale: 2 Value: 0.42
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 0.7 Scale: 2 Value: 0.49
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 0.8 Scale: 2 Value: 0.56
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 0.9 Scale: 2 Value: 0.63
Expected: 2 Max Scale: 2 Left Operand: 0.7 Right Operand: 1.0 Scale: 1 Value: 0.7
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 0.1 Scale: 2 Value: 0.08
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 0.2 Scale: 2 Value: 0.16
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 0.3 Scale: 2 Value: 0.24
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 0.4 Scale: 2 Value: 0.32
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 0.5 Scale: 1 Value: 0.4
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 0.6 Scale: 2 Value: 0.48
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 0.7 Scale: 2 Value: 0.56
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 0.8 Scale: 2 Value: 0.64
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 0.9 Scale: 2 Value: 0.72
Expected: 2 Max Scale: 2 Left Operand: 0.8 Right Operand: 1.0 Scale: 1 Value: 0.8
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 0.1 Scale: 2 Value: 0.09
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 0.2 Scale: 2 Value: 0.18
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 0.3 Scale: 2 Value: 0.27
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 0.4 Scale: 2 Value: 0.36
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 0.5 Scale: 2 Value: 0.45
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 0.6 Scale: 2 Value: 0.54
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 0.7 Scale: 2 Value: 0.63
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 0.8 Scale: 2 Value: 0.72
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 0.9 Scale: 2 Value: 0.81
Expected: 2 Max Scale: 2 Left Operand: 0.9 Right Operand: 1.0 Scale: 1 Value: 0.9
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 0.1 Scale: 1 Value: 0.1
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 0.2 Scale: 1 Value: 0.2
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 0.3 Scale: 1 Value: 0.3
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 0.4 Scale: 1 Value: 0.4
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 0.5 Scale: 1 Value: 0.5
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 0.6 Scale: 1 Value: 0.6
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 0.7 Scale: 1 Value: 0.7
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 0.8 Scale: 1 Value: 0.8
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 0.9 Scale: 1 Value: 0.9
Expected: 2 Max Scale: 2 Left Operand: 1.0 Right Operand: 1.0 Scale: 0 Value: 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment