two - php variable 2 decimal
PHP convertir decimal en fracción y volver? (14)
Quiero que el usuario pueda escribir una fracción como:
1/2
2 1/4
3
Y conviértalo a su decimal correspondiente, para guardarlo en MySQL, de esa manera puedo ordenar por él y hacer otras comparaciones con él.
Pero necesito poder convertir el decimal de nuevo a una fracción cuando se lo muestre al usuario
así que básicamente necesito una función que convierta una cadena de fracción a decimal:
fraction_to_decimal("2 1/4");// return 2.25
y una función que puede convertir un decimal en una cadena de facción:
decimal_to_fraction(.5); // return "1/2"
¿Cómo puedo hacer esto?
A veces necesitas encontrar una forma de hacerlo y el redondeo es aceptable. Entonces, si decide qué rango de redondeo funciona para usted, puede construir una función como esta. Para convertir un decimal en la fracción que más se aproxime. Puede ampliar la precisión agregando más denominadores para ser probado.
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;
}
Amigos, ¿puede esto ayudar?
[] s
function toFraction($number) {
if (!is_int($number)) {
$number = floatval($number);
$denominator = round(1 / $number);
return "1/{$denominator}";
}
else {
return $number;
}
}
Aquí hay una solución que primero determina una fracción válida (aunque no necesariamente la fracción más simple). Entonces 0.05 -> 5/100. Luego determina el mayor divisor común del numerador y el denominador para reducirlo a la fracción más simple, 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;
}
Esto incluye una implementación PHP pura del gcd, aunque si está seguro de que el módulo gmp está instalado, puede usar el que viene con gcd .
Como muchos otros han señalado, necesitas usar números racionales. Entonces, si convierte 1/7 a un decimal, luego intente convertirlo de nuevo a un decimal, no tendrá suerte porque la precisión perdida evitará que vuelva a 1/7. Para mis propósitos, esto es aceptable ya que todos los números con los que estoy tratando (mediciones estándar) son números racionales de todos modos.
Creo que también almacenaría la representación de la cadena, ya que, una vez que ejecutes los cálculos, ¡no la recuperarás!
Y, aquí hay una función de cálculo rápido y sucio, sin garantías:
$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;
¡Oh, para completar, agregue un cheque para asegurarse de que $fraction[''denominator''] != 0
.
Hice una publicación de blog con un par de soluciones para esto, el enfoque más reciente que tomé es: 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;
}
La fracción a decimal es bastante sencilla y hay muchas soluciones. Me gustaría recortar la cadena, reemplazar espacios con ''+'' y cualquier otra cosa que no sea espacio, / ,. o dígitos con '''' luego ejecutarlo a través de ''eval''.
El decimal a fracción es virtualmente imposible de hacer correctamente, no solo porque su fracción decimal probablemente se convierta primero en binario, momento en el que pierde mucha precisión. Como un ejercicio académico ... Si puedes vivir con la diferencia entre 20976/41953 y 1/2, entonces puedes probar una coincidencia aproximada para un número predefinido de fracciones:
(Probablemente haya una forma más ordenada de implementar el mismo algoritmo, pero lo dejaré como un ejercicio para el lector).
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;
}
}
}
}
Pero personalmente, solo almacenaría la representación original en un campo separado en la base de datos.
Para poder usar la clase Math_Fraction de PEAR para algunas de sus necesidades
<?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();
?>
Poca mejora sobre el anterior, pero manteniéndolo simple.
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;
}
Solo agregando un poco más de lógica a la respuesta aceptada de Derek: verifique la "división por cero" y la verificación de entrada de números enteros.
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;
}
Tendrá que enfrentarse a un problema grave, porque los flotadores no son lo suficientemente precisos.
Cuando tenga que lidiar con 1.3333
, PHP hará una estimación de este valor ... Por lo tanto, nunca podrá convertirlo a 1 1/3
.
Parece que es fácil de superar, pero si quieres que tu programa 1/7901
( ~ 1,2656625743576762435134793064169e-4
) con 1/7907
( ~ 1,2647021626406981155937776653598e-4
) precisamente ... ¡esto será un verdadero infierno! !
En mi humilde opinión, si quieres lidiar con las matemáticas, debes confiar en una biblioteca externa ... o intentar que PHP se comunique con Matlab.
Si quieres saber más, te sugiero que busques problemas de punto flotante ... Comenzando con wikipedia .
Un enfoque sería recuperar el valor decimal y multiplicarlo por 2, 3, 4 y así sucesivamente hasta obtener un número entero.
Sin embargo, me quedo con la respuesta dada por Derek. Adivina qué sucede cuando un usuario inserta n / (n + 1) con n alto. Dicho algoritmo tendría que escanear todos los números hasta n + 1. Sin mencionar que es probable que termine con problemas de aproximación.
Una variación del enfoque de Jir podría funcionar si solo se utiliza una cantidad limitada de denominadores: multiplique todo por los menos comunes denominadores (y redondee el resultado para descartar los decimales restantes debido a la aproximación).
Es decir: si solo tienes que lidiar con medias, tres y tres cuartos, simplemente multiplica todo por 12.
Y también si conoce el denominador común, esto debería reducir en gran medida la velocidad de búsqueda al saber exactamente qué números buscar en lugar de buscar todos los n + 1 posibles.
Si tiene que lidiar con muchas fracciones inusuales, como 1/7, 1/13, etc., siga la solución de Derek y almacene el valor original también.
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);
}
Entonces $f == $n / $d
Por ejemplo:
print_r(dec2frac(3.1415926));
Salidas:
Array
(
[0] => 3537118815677477 // $n
[1] => 1125899906842624 // $d
)
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;
}