I would like to convert a 17 digit unique ID retrieved as a string from my MYSQL database to a number. I use the int to do so. However I get a zero at the end:
$num = 96435171263250434;
(int)$num --> 96435171263250430
I've checked I am running a 64 bit system. I get the following:
php -r 'echo PHP_INT_MAX;'
9223372036854775807
How do I fix this issue???
You cannot exceed PHP_INT_MAX
$num = "96435171263250434";
$x = (float) $num; // This should hold it but it's a float
$maxIntMult = 0;
$maxIntMult = intval($x / PHP_INT_MAX);
$remainder = $x - $maxIntMult * PHP_INT_MAX;
echo PHP_INT_MAX . " x " .$maxIntMult. " + " . $remainder; // function of two integer if you can't work with floats and you can make something of this
You can try to make use of the fact that ids don't have negative values, effectively doubling your range.
$num = PHP_INT_MAX + 50;
$x = (float) $num;
$intX = $num - PHP_INT_MAX;
echo $intX; // Shows 50 with the '0' being -PHP_INT_MAX
function getIdWithNonZeroOffset($stringId)
{
$x = (float) $stringId;
$intX = $x - PHP_INT_MAX;
return $intX;
}
function getStringFromNonZeroOfssetId($id)
{
return (string) ($id + PHP_INT_MAX);
}
echo getIdWithNonZeroOffset((string)(PHP_INT_MAX + 200)); // Gives 200 (store this in int column)
echo getStringFromNonZeroOfssetId(200); // Gives "2147483847" (my max int is "2147483647")
Related
math with bitcoin is giving me problems
$value = bcmul((float)$TotalMoney, $p,8);
$value = bcdiv((float)$Value, 100,8);
returns 8.431e-05 as one of the values in the script
i've tried
$newNum = (float)$value;
$newNum = number_format((float)$value, 8);
$newNum = sprintf('%.8f',$value);
function scientific_notation($in_float_value, $in_decimal_place_count = -1)
{
// Get the exponent
$abs_float_value = abs($in_float_value);
$exponent = floor($abs_float_value == 0 ? 0 : log10($abs_float_value));
// Scale to get the mantissa
$in_float_value *= pow(10, -$exponent);
// Create the format string based
// on the requested number of decimal places.
$format = ($in_decimal_place_count >= 0) ? "." . $in_decimal_place_count : "";
//echo("Format0: $format");
// Format the exponent part using zero padding.
$formatted_exponent = "+" . sprintf("%02d", $exponent);
if($exponent < 0.0)
{
$formatted_exponent = "-" . sprintf("%02d", -$exponent);
}
$format = "%" . $format . "fe%s";
//echo("Format1: $format");
// Return the final value combining mantissa and exponent
return sprintf($format, $in_float_value, $exponent);
}
$newNum = scientific_notation($value,8);
Tried it in phpfiddle and it works. maybe the problem is storing it in a db. It's stores as 8.431e-05 in the database
what am I doing wrong?
Use the exemple below to convert Scientific Notation to float/decimal on PHP:
echo sprintf('%f', floatval('-1.0E-5'));//default 6 decimal places
echo sprintf('%.8f', floatval('-1.0E-5'));//force 8 decimal places
echo rtrim(sprintf('%f',floatval(-1.0E-5)),'0');//remove trailing zeros
When working with Bitcoin balances it is recommended to store amounts in a database in satoshis as an integer and then you can convert it back to 8 decimals when displaying it on the screen to users.
$amount = 0.0132;
$convert = $amount * 100000000;
// store in DB as the converted amount 1320000 as an integer
// when grabbing from DB convert it back
$databaseValue = 1320000;
$convertBack = $databaseValue / 100000000;
$display = number_format($convertBack, 8);
echo $display;
I've searched through a number of similar questions, but unfortunately I haven't been able to find an answer to this problem. I hope someone can point me in the right direction.
I need to come up with a PHP function which will produce a random number within a set range and mean. The range, in my case, will always be 1 to 100. The mean could be anything within the range.
For example...
r = f(x)
where...
r = the resulting random number
x = the mean
...running this function in a loop should produce random values where the average of the resulting values should be very close to x. (The more times we loop the closer we get to x)
Running the function in a loop, assuming x = 10, should produce a curve similar to this:
+
+ +
+ +
+ +
+ +
Where the curve starts at 1, peeks at 10, and ends at 100.
Unfortunately, I'm not well versed in statistics. Perhaps someone can help me word this problem correctly to find a solution?
interesting question. I'll sum it up:
We need a funcion f(x)
f returns an integer
if we run f a million times the average of the integer is x(or very close at least)
I am sure there are several approaches, but this uses the binomial distribution: http://en.wikipedia.org/wiki/Binomial_distribution
Here is the code:
function f($x){
$min = 0;
$max = 100;
$curve = 1.1;
$mean = $x;
$precision = 5; //higher is more precise but slower
$dist = array();
$lastval = $precision;
$belowsize = $mean-$min;
$abovesize = $max-$mean;
$belowfactor = pow(pow($curve,50),1/$belowsize);
$left = 0;
for($i = $min; $i< $mean; $i++){
$dist[$i] = round($lastval*$belowfactor);
$lastval = $lastval*$belowfactor;
$left += $dist[$i];
}
$dist[$mean] = round($lastval*$belowfactor);
$abovefactor = pow($left,1/$abovesize);
for($i = $mean+1; $i <= $max; $i++){
$dist[$i] = round($left-$left/$abovefactor);
$left = $left/$abovefactor;
}
$map = array();
foreach ($dist as $int => $quantity) {
for ($x = 0; $x < $quantity; $x++) {
$map[] = $int;
}
}
shuffle($map);
return current($map);
}
You can test it out like this(worked for me):
$results = array();
for($i = 0;$i<100;$i++){
$results[] = f(20);
}
$average = array_sum($results) / count($results);
echo $average;
It gives a distribution curve that looks like this:
I'm not sure if I got what you mean, even if I didn't this is still a pretty neat snippet:
<?php
function array_avg($array) { // Returns the average (mean) of the numbers in an array
return array_sum($array)/count($array);
}
function randomFromMean($x, $min = 1, $max = 100, $leniency = 3) {
/*
$x The number that you want to get close to
$min The minimum number in the range
$max Self-explanatory
$leniency How far off of $x can the result be
*/
$res = [mt_rand($min,$max)];
while (true) {
$res_avg = array_avg($res);
if ($res_avg >= ($x - $leniency) && $res_avg <= ($x + $leniency)) {
return $res;
break;
}
else if ($res_avg > $x && $res_avg < $max) {
array_push($res,mt_rand($min, $x));
}
else if ($res_avg > $min && $res_avg < $x) {
array_push($res, mt_rand($x,$max));
}
}
}
$res = randomFromMean(22); // This function returns an array of random numbers that have a mean close to the first param.
?>
If you then var_dump($res), You get something like this:
array (size=4)
0 => int 18
1 => int 54
2 => int 22
3 => int 4
EDIT: Using a low value for $leniency (like 1 or 2) will result in huge arrays, since testing, I recommend a leniency of around 3.
I need to implement a function that interpolates an exponential curve from three points, but I'm not sure how to do it.
I have a graph that has the Y axis as percentage, 0 to 100% and X as 0 to 10.
The only points that I know are (50,7), (100,10) and (0,0).
I know I can create an array that has the percentages and values and loop through it, but this does not feel like the "right" way to do it. Is there a more direct algorithm?
I would use the formula :
partial : total = % : 100
partial (the value) = (total * %) / 100
Code
<?php
$points = array("8%,67%","36%,74%","73%,13%");
function return_value($percentage,$total) {
$value = ($total * $percentage) / 100.0;
return $value;
}
function evaluate_points($points) {
$max_x = 100.0; // As float value
$max_y = 10.0; // As float value
for ($point = 0; $point < count($points); $point++) {
//Replace the % sign
$points[$point] = str_replace("%", "", $points[$point]);
$point_percentages = explode(",", $points[$point]);
$x_percentage = $point_percentages[0];
$y_percentage = $point_percentages[1];
echo("The value for x is : ".return_value($x_percentage,$max_x) ."<br>");
echo("The value for y is : ".return_value($y_percentage,$max_y). "<br><br>");
}
}
evaluate_points($points);
?>
Output
I am using the following PHP code to calculate a CRN for BPay:
<?php
function LuhnCalc($number) {
$chars = array_reverse(str_split($number, 1));
$odd = array_intersect_key($chars, array_fill_keys(range(1, count($chars), 2), null));
$even = array_intersect_key($chars, array_fill_keys(range(0, count($chars), 2), null));
$even = array_map(function($n) { return ($n >= 5)?2 * $n - 9:2 * $n; }, $even);
$total = array_sum($odd) + array_sum($even);
return ((floor($total / 10) + 1) * 10 - $total) % 10;
}
print LuhnCalc($_GET['num']);
?>
However it seems that BPAY is version 5 of MOD 10, for which I can't find any documentation. It seems to not be the same as MOD10.
The following numbers where tested:
2005,1597,3651,0584,9675
bPAY
2005 = 20052
1597 = 15976
3651 = 36514
0584 = 05840
9675 = 96752
MY CODE
2005 = 20057
1597 = 15974
3651 = 36517
0584 = 05843
9675 = 96752
As you can see, none of them match the BPAY numbers.
This PHP function will generate BPay reference numbers based on the mod10 version 5 algorithm.
Who knows why BPay can't add this to their website. I only found an explanation by googling finding the algorithm being called "MOD10V05" instead of "Mod 10 version 5".
function generateBpayRef($number) {
$number = preg_replace("/\D/", "", $number);
// The seed number needs to be numeric
if(!is_numeric($number)) return false;
// Must be a positive number
if($number <= 0) return false;
// Get the length of the seed number
$length = strlen($number);
$total = 0;
// For each character in seed number, sum the character multiplied by its one based array position (instead of normal PHP zero based numbering)
for($i = 0; $i < $length; $i++) $total += $number{$i} * ($i + 1);
// The check digit is the result of the sum total from above mod 10
$checkdigit = fmod($total, 10);
// Return the original seed plus the check digit
return $number . $checkdigit;
}
Here's a way of implementing the "MOD10V5" algorithm (or "mod 10 version 5") using a t-sql user defined function in SQL server. It accepts a Customer ID up to 9 characters long, and return an 11 character CRN (Customer Reference Number).
I also prepended a version number onto the start of my CustomerID, you could do this too if you think you might end up changing it in the future.
CREATE Function [dbo].[CalculateBPayCRN]
(
#CustomerID nvarchar(9)
)
RETURNS varchar(11)
AS
BEGIN
DECLARE #NewCRN nvarchar(11)
DECLARE #Multiplier TINYINT
DECLARE #Sum int
DECLARE #SubTotal int
DECLARE #CheckDigit int
DECLARE #ReturnVal BIGINT
SELECT #Multiplier = 1
SELECT #SubTotal = 0
-- If it's less than 9 characters, pad it with 0's, then prepend a '1'
SELECT #NewCRN = '1' + right('000000000'+ rtrim(#CustomerID), 9)
-- loop through each digit in the #NewCRN, multiple it by the correct weighting and subtotal it:
WHILE #Multiplier <= LEN(#NewCRN)
BEGIN
SET #Sum = CAST(SUBSTRING(#NewCRN,#Multiplier,1) AS TINYINT) * #Multiplier
SET #SubTotal = #SubTotal + #Sum
SET #Multiplier = #Multiplier + 1
END
-- mod 10 the subtotal and the result is our check digit
SET #CheckDigit = #SubTotal % 10
SELECT #ReturnVal = #NewCRN + cast(#CheckDigit as varchar)
RETURN #ReturnVal
END
GO
Modula 10 V1 in PHP. Tested against my Windows dataflex routine and it is the same.
function generateBpayRef($number) {
//Mod 10 v1
$number = preg_replace("/\D/", "", $number);
// The seed number needs to be numeric
if(!is_numeric($number)) return false;
// Must be a positive number
if($number <= 0) return false;
$stringMemberNo = "$number";
$stringMemberNo = str_pad($stringMemberNo, 6, "0", STR_PAD_LEFT);
//echo " Padded Number is $stringMemberNo ";
$crn = $stringMemberNo;
for($i=0;$i<7;$i++){
$crnval = substr($crn,(5-$i),1);
$iPartVal = $iWeight * $crnval;
if($iPartVal>9){
//echo " Greater than 9: $iPartVal ";
$firstChar = substr($iPartVal,0,1);
$secondChar = substr($iPartVal,1,1);
$iPartVal=$firstChar+$secondChar;
//$iPartVal -= 9;
}
$iSum+=$iPartVal;
$iWeight++;
if ($iWeight>2){$iWeight=1;}
//echo " CRN: $crnval ] Weight: $iWeight ] Part: $iPartVal ] SUM: $iSum ";
}
$iSum %= 10;
if($iSum==0){
//echo " zero check is $iSum ";
//return $iSum;
}
else{
//return 10-$iSum;
$iSum=(10-$iSum);
}
//echo " Check is a $iSum ";
$BpayMemberNo = $stringMemberNo . $iSum ;
echo " New: $BpayMemberNo ";
return ($BpayMemberNo);
}
Here is a ruby class I whipped up quickly for Mod 10 v5
module Bpay
class CRN
attr_accessor :number, :crn
class << self
def calculate_for(number)
new(number).crn
end
end
def initialize(number)
#number = number
calculate
end
def calculate
raise ArgumentError, "The number '#{number}' is not valid" unless valid?
digits = number.to_s.scan(/\d/).map { |x| x.to_i }
raise ArgumentError, "The number '#{number}' must be at least 2 digits in length" if digits.size < 2
check_digit = digits.each_with_index.map { |d, i| d * (i + 1) }.inject(:+) % 10
#crn = "#{number}#{check_digit}"
end
def valid?
return false unless !!Integer(number.to_s) rescue false
return false if number.to_i <= 0
true
end
end
end
This is in C#, but this is what I have so far for BPay check digit generation:
private void btnBPayGenerate_Click(object sender, EventArgs e)
{
var originalChars = txtBPayNumber.Text.ToCharArray();
List<int> oddDigits = new List<int>();
List<int> evenDigits = new List<int>();
int oddTotal = 0, evenTotal = 0, total = 0, checkDigit ;
const int oddMultiplier = 3;
const int modulus = 10;
bool isOdd = true;
for (int x = 0; x < originalChars.Length; x++)
{
if(isOdd)
oddDigits.Add(Int32.Parse(originalChars[x].ToString()));
else
evenDigits.Add(Int32.Parse(originalChars[x].ToString()));
isOdd = !isOdd;
}
foreach (var digit in oddDigits)
oddTotal += digit;
foreach (var digit in evenDigits)
evenTotal += digit;
oddTotal = oddTotal * oddMultiplier;
total = oddTotal + evenTotal;
checkDigit = (modulus - (total % modulus));
lblBPayResult.Text = txtBPayNumber.Text + checkDigit.ToString();
}
I haven't completed testing this yet, I will post back once BPAY get back to me.
EDIT: try this: https://gist.github.com/1287893
I had to work out a version for javascript, this is what I came up with. It correctly generates the expected numbers in the original question.
var startingNumber = 2005;
var reference = startingNumber.toString();
var subTotal = 0;
for (var x = 0; x < reference.length; x++) {
subTotal += (x + 1) * reference.charAt(x);
}
var digit = subTotal % 10;
var bpayReference = reference + digit.toString();
Here is a function I created using vb.net to calculate a mod 10 version 5 check digit
Private Function CalcCheckDigit(ByRef psBaseNumber As String) As String
Dim lCheckDigit, iLoop As Integer
Dim dCalcNumber As Double
lCheckDigit = 0
dCalcNumber = 0
For iLoop = 0 To (psBaseNumber.Length - 1)
lCheckDigit = lCheckDigit + (psBaseNumber.Substring(iLoop, 1) * (iLoop + 1))
Next iLoop
lCheckDigit = lCheckDigit Mod 10
CalcCheckDigit = psBaseNumber & CStr(lCheckDigit)
End Function
$temp is currently 6. But the variable result can be changing every time to a different number so it is not a fixed value.
Anyway, for this $temp * 1.1666666, the result will be 6.99999996. Since I used the floor function, it will be rounded down to 6.
Is there any way when the value is more then>*.49999 it will stay at *.5 instead of *?
Example: 6.51111111, 6.78948123, 6.9747124
Expected Output: 6.5
Example: 6.49999999, 6.12412431, 6.33452361
Expected Output: 6
Do note that, $temp value will be ever changing..thank you!
Use round($number, 1). That will round to the nearest decimal point.
$number = round(.1666666 * $temp, 1);
If you want to round to the nearest half you can do this:
function round_to_half($num)
{
if($num >= ($half = ($ceil = ceil($num))- 0.5) + 0.25) return $ceil;
else if($num < $half - 0.25) return floor($num);
else return $half;
}
$number = round_to_half(.1666666 * $temp);
Try this code...
<?php
$temp = 6.94444;
echo myRound($temp);
function myRound($temp)
{
$frac = $temp - floor($temp);
$frac = ($frac >= .5) ? .5 : 0;
return ( floor($temp) + $frac );
}
?>
Hope this is what you want.