我希望用户能够输入如下部分:
1/2
2 1/4
3
并将其转换为相应的十进制数,以便保存在MySQL中,这样我就可以通过它进行排序并对其进行其他比较。
但是我需要能够在向用户显示时将小数转换回分数
所以基本上我需要一个将分数字符串转换为十进制的函数:
fraction_to_decimal("2 1/4");// return 2.25
和一个可以将小数转换为派系字符串的函数:
decimal_to_fraction(.5); // return "1/2"
我该怎么做?
答案 0 :(得分:19)
我想我也会存储字符串表示,因为一旦你运行数学运算,你就不会收回它!
而且,这是一个快速n-dirty计算函数,无法保证:
$input = '1 1/2';
$fraction = array('whole' => 0);
preg_match('/^((?P<whole>\d+)(?=\s))?(\s*)?(?P<numerator>\d+)\/(?P<denominator>\d+)$/', $input, $fraction);
$result = $fraction['whole'] + $fraction['numerator']/$fraction['denominator'];
print_r($result);die;
哦,为了完整起见,请添加一项检查以确保$fraction['denominator'] != 0
。
答案 1 :(得分:18)
有时您需要找到一种方法来实现它并且可以接受舍入。因此,如果你决定为你做什么范围的舍入,你可以建立这样的函数。将小数转换为最接近匹配的小数。您可以通过添加更多要测试的分母来扩展准确度。
function decToFraction($float) {
// 1/2, 1/4, 1/8, 1/16, 1/3 ,2/3, 3/4, 3/8, 5/8, 7/8, 3/16, 5/16, 7/16,
// 9/16, 11/16, 13/16, 15/16
$whole = floor ( $float );
$decimal = $float - $whole;
$leastCommonDenom = 48; // 16 * 3;
$denominators = array (2, 3, 4, 8, 16, 24, 48 );
$roundedDecimal = round ( $decimal * $leastCommonDenom ) / $leastCommonDenom;
if ($roundedDecimal == 0)
return $whole;
if ($roundedDecimal == 1)
return $whole + 1;
foreach ( $denominators as $d ) {
if ($roundedDecimal * $d == floor ( $roundedDecimal * $d )) {
$denom = $d;
break;
}
}
return ($whole == 0 ? '' : $whole) . " " . ($roundedDecimal * $denom) . "/" . $denom;
}
答案 2 :(得分:8)
可以将PEAR的Math_Fraction类用于某些的需求
<?php
include "Math/Fraction.php";
$fr = new Math_Fraction(1,2);
// print as a string
// output: 1/2
echo $fr->toString();
// print as float
// output: 0.5
echo $fr->toFloat();
?>
答案 3 :(得分:3)
这是首先确定有效分数的解决方案(尽管不一定是最简单的分数)。所以0.05 - > 5/100。然后它确定分子和分母的最大公约数,将其减少到最简单的1/20。
function decimal_to_fraction($fraction) {
$base = floor($fraction);
$fraction -= $base;
if( $fraction == 0 ) return $base;
list($ignore, $numerator) = preg_split('/\./', $fraction, 2);
$denominator = pow(10, strlen($numerator));
$gcd = gcd($numerator, $denominator);
$fraction = ($numerator / $gcd) . '/' . ($denominator / $gcd);
if( $base > 0 ) {
return $base . ' ' . $fraction;
} else {
return $fraction;
}
}
# Borrowed from: http://www.php.net/manual/en/function.gmp-gcd.php#69189
function gcd($a,$b) {
return ($a % $b) ? gcd($b,$a % $b) : $b;
}
这包括gcd的纯PHP实现,但如果您确定已安装gmp模块,则可以use the one that comes with gcd。
正如其他许多人所说,你需要使用有理数。因此,如果您将1/7转换为小数,然后尝试将其转换回小数,那么您将失去运气,因为精度损失将阻止它回到1/7。对于我的目的,这是可以接受的,因为我正在处理的所有数字(标准测量)无论如何都是有理数。
答案 4 :(得分:2)
上面几乎没有改进,但保持简单。
function dec2frac($f) {
$base = floor($f);
if ($base) {
$out = $base . ' ';
$f = $f - $base;
}
if ($f != 0) {
$d = 1;
while (fmod($f, 1) != 0.0) {
$f *= 2;
$d *= 2;
}
$n = sprintf('%.0f', $f);
$d = sprintf('%.0f', $d);
$out .= $n . '/' . $d;
}
return $out;
}
答案 5 :(得分:1)
[]中
function toFraction($number) {
if (!is_int($number)) {
$number = floatval($number);
$denominator = round(1 / $number);
return "1/{$denominator}";
}
else {
return $number;
}
}
答案 6 :(得分:0)
一种方法是检索十进制值并将其乘以2,3,4等,直到得到一个整数。
但是,我坚持Derek给出的答案。猜猜当用户插入n /(n + 1)n为高时会发生什么。这样的算法必须扫描所有数字直到n + 1。 更不用说你很可能会遇到近似问题。
答案 7 :(得分:0)
你将不得不面对一个严重的问题,因为花车不够精确。
当您必须处理1.3333
时,PHP会对此值进行估算......因此您永远无法将其转换为1 1/3
。
似乎很容易克服,但如果您希望自己的程序能够将1/7901
(~ 1,2656625743576762435134793064169e-4
)与1/7907
(~
1,2647021626406981155937776653598e-4
)区别开来......这将是真是个地狱!!
恕我直言,如果你想处理数学,你应该依赖外部库......或者尝试让PHP与Matlab进行通信。
如果你想了解更多,我建议你挖掘浮点问题......从wikipedia开始。
答案 8 :(得分:0)
如果仅使用有限数量的分母,Jir方法的变体实际上可以起作用:将所有分数乘以最小公分母(并将结果四舍五入以丢弃由于近似值而产生的任何剩余小数)。
即:如果你只需要对付半身,女人和四分之一,只需将所有东西都乘以12。
如果您知道共同点,那么通过确切地知道要搜索哪些数字而不是搜索所有n + 1可能会大大降低搜索速度。
如果你必须处理很多不寻常的分数,比如1 / 7,1 / 13等,请坚持使用Derek的解决方案并存储原始值。
答案 9 :(得分:0)
小数到十进制是非常简单的,有很多解决方案。我会修剪字符串,用'+'替换空格,以及除空格之外的任何东西,/。或'''然后通过'eval'运行它的数字。
小数到分数几乎不可能正确执行 - 尤其是因为您的小数部分可能必须首先转换为二进制 - 此时您会失去很多精度。作为学术练习.....如果你可以忍受20976/41953和1/2之间的差异,那么你可以尝试模糊匹配预定数量的分数:
(可能有一种更简洁的方法来实现相同的算法 - 但我会把它作为读者的练习。)
define('DECIMAL_DIGITS',5);
function decimal_2_frac($inp_decimal)
{
static $fracs;
if (!is_array($fracs)) {
init_fracs($fracs);
}
$int_part=(integer)$inp_decimal;
$inp_decimal=$inp_decimal-$int_part;
$candidate='';
$distance=10;
foreach ($fracs as $decimal=>$frac) {
if (abs($decimal-$inp_decimal)<$distance) {
$candidate=$frac;
$distance=abs($decimal-$inp_decimal);
}
if (abs($decimal-$inp_decimal)>$distance) {
break;
}
}
return $int_part . ' ' . $candidate;
}
function init_fracs(&$fracs)
{
$fracs=array();
for ($x=2;$x<(5*DECIMAL_DIGITS);$x++) {
// there's probably a beter way to calculate the loop limit
for ($y=1; $y<$x; $y++) {
$decimal=round($y/$x,DECIMAL_DIGITS);
$frac="$x/$y";
if (!array_key_exists($decimal,$fracs)) {
$fracs[$decimal]=$frac;
}
}
}
}
但就个人而言,我只是将原始表示存储在数据库的单独字段中。
答案 10 :(得分:0)
function dec2frac($f)
{
$d = 1
while (fmod($f, 1) != 0.0) {
$f *= 2;
$d *= 2;
}
$n = sprintf('%.0f', $f);
$d = sprintf('%.0f', $d);
return array($n, $d);
}
然后$f == $n / $d
例如:
print_r(dec2frac(3.1415926));
输出:
Array
(
[0] => 3537118815677477 // $n
[1] => 1125899906842624 // $d
)
答案 11 :(得分:0)
我发了一篇博文,其中有几个解决方案,我采用的最新方法是: http://www.carlosabundis.com/2014/03/25/converting-decimals-to-fractions-with-php-v2/
function dec2fracso($dec){
//Negative number flag.
$num=$dec;
if($num<0){
$neg=true;
}else{
$neg=false;
}
//Extracts 2 strings from input number
$decarr=explode('.',(string)$dec);
//Checks for divided by zero input.
if($decarr[1]==0){
$decarr[1]=1;
$fraccion[0]=$decarr[0];
$fraccion[1]=$decarr[1];
return $fraccion;
}
//Calculates the divisor before simplification.
$long=strlen($decarr[1]);
$div="1";
for($x=0;$x<$long;$x++){
$div.="0";
}
//Gets the greatest common divisor.
$x=(int)$decarr[1];
$y=(int)$div;
$gcd=gmp_strval(gmp_gcd($x,$y));
//Calculates the result and fills the array with the correct sign.
if($neg){
$fraccion[0]=((abs($decarr[0])*($y/$gcd))+($x/$gcd))*(-1);
}else{
$fraccion[0]=(abs($decarr[0])*($y/$gcd))+($x/$gcd);
}
$fraccion[1]=($y/$gcd);
return $fraccion;
}
答案 12 :(得分:0)
为Derek接受的答案添加更多逻辑 - 检查&#34;除以零&#34;和整数输入检查。
function fractionToDec($input) {
if (strpos($input, '/') === FALSE) {
$result = $input;
} else {
$fraction = array('whole' => 0);
preg_match('/^((?P<whole>\d+)(?=\s))?(\s*)?(?P<numerator>\d+)\/(?P<denominator>\d+)$/', $input, $fraction);
$result = $fraction['whole'];
if ($fraction['denominator'] > 0)
$result += $fraction['numerator'] / $fraction['denominator'];
}
return $result;
}
答案 13 :(得分:0)
function frac2dec($fraction) {
list($whole, $fractional) = explode(' ', $fraction);
$type = empty($fractional) ? 'improper' : 'mixed';
list($numerator, $denominator) = explode('/', $type == 'improper' ? $whole : $fractional);
$decimal = $numerator / ( 0 == $denominator ? 1 : $denominator );
return $type == 'improper' ? $decimal : $whole + $decimal;
}
答案 14 :(得分:0)
使用第 3 方库,例如: https://packagist.org/packages/phospr/fraction
用法:
$fraction = Fraction::fromFloat(1.5);
echo "Fraction is: " . $fraction->getNumerator() . '/' . $fraction->getDenominator();
echo "Float is: " . $fraction->toFloat();
我通常会在 https://packagist.org 上进行快速搜索,看看是否已经存在可以解决我正在尝试做的事情的东西,如果存在,那么我可以利用社区已经投入解决的大量时间问题(这将比我能够投入的时间多得多)而且它也更有可能没有错误,已经过其他人的实战测试,甚至可能有一个测试套件覆盖它。< /p>
节省时间并提高质量。