Dezimalzahlen in Javascript abschneiden (nicht abrunden)

91

Ich versuche, Dezimalzahlen auf Dezimalstellen zu kürzen. Etwas wie das:

5.467   -> 5.46  
985.943 -> 985.94

toFixed(2)macht genau das Richtige, aber es rundet den Wert ab. Ich brauche den Wert nicht abgerundet. Hoffe das ist in Javascript möglich.

kcssm
quelle
6
jQuery ist nur ein Framework und Ihr Problem hängt nicht mit jQuery zusammen. Es geht mehr darum, einige grundlegende Berechnungen in JavaScript durchzuführen. Ich hoffe, Sie sind auch mit einer Nicht-jQuery-Lösung zufrieden.
Felix Kling
Ich fand es zu viel Arbeit, um meine Berechnungen dazu zu bringen, mit Javascript nur zwei Dezimalstellen zurückzugeben. Ich konnte es stattdessen einfach in meiner Datenbankansicht tun. Mir ist klar, dass diese Methode nicht in jede Situation passt, aber ich möchte sie hier veröffentlichen, weil sie jemandem viel Zeit sparen könnte.
MsTapp

Antworten:

50

upd :

Es hat sich also herausgestellt, dass Rundungsfehler Sie immer verfolgen werden, egal wie sehr Sie versuchen, sie zu kompensieren. Daher sollte das Problem angegriffen werden, indem Zahlen genau in Dezimalschreibweise dargestellt werden.

Number.prototype.toFixedDown = function(digits) {
    var re = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)"),
        m = this.toString().match(re);
    return m ? parseFloat(m[1]) : this.valueOf();
};

[   5.467.toFixedDown(2),
    985.943.toFixedDown(2),
    17.56.toFixedDown(2),
    (0).toFixedDown(1),
    1.11.toFixedDown(1) + 22];

// [5.46, 985.94, 17.56, 0, 23.1]

Alte fehleranfällige Lösung basierend auf der Zusammenstellung anderer:

Number.prototype.toFixedDown = function(digits) {
  var n = this - Math.pow(10, -digits)/2;
  n += n / Math.pow(2, 53); // added 1360765523: 17.56.toFixedDown(2) === "17.56"
  return n.toFixed(digits);
}
Kirilloid
quelle
4
Ja, Prototypen funktionieren nicht zuverlässig browserübergreifend. Anstatt diese (eingeschränkte) Funktion über das Typsystem auf eine Weise zu definieren, die nicht zuverlässig funktioniert, sollten Sie sie einfach in eine Bibliothek stellen.
Thomas W
3
Dies funktioniert nicht wie ausgenommen. Versuchen Sie es mit der Zahl 17.56 und den Ziffern = 2. Es sollte 17.56 sein, aber diese Funktion gibt 17.55 zurück.
Shendz
2
Zwei Widersprüche mit dieser Funktion: Diese Funktion liefert einen String so 1.11.toFixedDown(1) + 22endet wie 1.122statt 23.1. Sollte auch 0.toFixedDown(1)produzieren, 0aber stattdessen produziert es -0.1.
Nick Knowlson
5
Beachten Sie, dass diese Funktion das negative Vorzeichen entfernt. Bsp. : (-10.2131).toFixedDown(2) // ==> 10.21.
Usandfriends
4
Auch (1e-7).toFixedDown(0) // ==> 1e-7. Heißt das für 1e-(>=7)(zB: 1e-8, 1e-9, ...).
usandfriends
60

Dogberts Antwort ist gut, aber wenn Ihr Code möglicherweise mit negativen Zahlen umgehen Math.floormuss, kann dies zu unerwarteten Ergebnissen führen.

ZB Math.floor(4.3) = 4aberMath.floor(-4.3) = -5

Verwenden Sie stattdessen eine Hilfsfunktion wie diese, um konsistente Ergebnisse zu erzielen:

truncateDecimals = function (number) {
    return Math[number < 0 ? 'ceil' : 'floor'](number);
};

// Applied to Dogbert's answer:
var a = 5.467;
var truncated = truncateDecimals(a * 100) / 100; // = 5.46

Hier ist eine bequemere Version dieser Funktion:

truncateDecimals = function (number, digits) {
    var multiplier = Math.pow(10, digits),
        adjustedNum = number * multiplier,
        truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);

    return truncatedNum / multiplier;
};

// Usage:
var a = 5.467;
var truncated = truncateDecimals(a, 2); // = 5.46

// Negative digits:
var b = 4235.24;
var truncated = truncateDecimals(b, -2); // = 4200

Wenn dies nicht erwünscht ist, fügen Sie in Math.absder ersten Zeile einen Anruf ein :

var multiplier = Math.pow(10, Math.abs(digits)),

BEARBEITEN : shendz weist richtig darauf hin, dass die Verwendung dieser Lösung mit a = 17.56falsch erzeugt 17.55. Weitere Informationen dazu finden Sie unter Was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte . Leider ist es mit Javascript ziemlich schwierig, eine Lösung zu schreiben, die alle Ursachen für Gleitkommafehler beseitigt. In einer anderen Sprache würden Sie Ganzzahlen oder vielleicht einen Dezimaltyp verwenden, aber mit Javascript ...

Diese Lösung sollte 100% genau sein, ist aber auch langsamer:

function truncateDecimals (num, digits) {
    var numS = num.toString(),
        decPos = numS.indexOf('.'),
        substrLength = decPos == -1 ? numS.length : 1 + decPos + digits,
        trimmedResult = numS.substr(0, substrLength),
        finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;

    return parseFloat(finalResult);
}

Für diejenigen, die Geschwindigkeit benötigen, aber auch Gleitkommafehler vermeiden möchten, versuchen Sie etwas wie BigDecimal.js . Weitere Javascript BigDecimal-Bibliotheken finden Sie in dieser SO-Frage: "Gibt es eine gute Javascript BigDecimal-Bibliothek?" und hier ist ein guter Blog-Beitrag über Mathematikbibliotheken für Javascript

Nick Knowlson
quelle
Warum unerwartet? Das Ändern der Rundungsrichtung, wenn Sie unter 0 gehen, führt zu allen möglichen arithmetischen Artefakten und beschissener Mathematik. Beispielsweise werden doppelt so viele Zahlen wie jede andere Ganzzahl auf 0 gerundet. Für Grafiken, Buchhaltung und viele andere Zwecke erhalten Sie beschissene Ergebnisse. Um die Wahrheit zu sagen, wäre es schwieriger zu sagen, wozu Ihr Vorschlag gut ist, als zu sagen, was nicht .
Thomas W
Es ist gut für genau das, was es sagt - wenn Sie Dezimalstellen abschneiden möchten, anstatt zu runden.
Nick Knowlson
1
Wird nicht mit
17.56
Guter Punkt Shendz. Ich habe meine Antwort mit einer Lösung aktualisiert, die alle Gleitkommafehler für diejenigen beseitigt, die dies benötigen.
Nick Knowlson
1
Dies funktioniert nicht für Zahlen, die kleiner als 1 sind, wenn Sie keine Dezimalstellen möchten. TruncateDecimals (.12345, 0) führt zu NaN, es sei denn, Sie fügen eine Prüfung hinzu: if(isNAN(result) result = 0; Abhängig vom gewünschten Verhalten.
Jeremy Witmer
34
var a = 5.467;
var truncated = Math.floor(a * 100) / 100; // = 5.46
Dogbert
quelle
5
Dies funktioniert gut, führt jedoch zu Ergebnissen, die wahrscheinlich unerwünscht sind, wenn er (oder jemand anderes, der sich diese Antwort später ansieht) mit negativen Zahlen umgehen muss. Siehe stackoverflow.com/a/9232092/224354
Nick Knowlson
2
Warum unerwünscht? Das Ändern der Rundungsrichtung, wenn Sie unter 0 gehen, führt zu allen möglichen arithmetischen Artefakten.
Thomas W
7
Es gibt einen Unterschied zwischen Runden und Abschneiden. Abschneiden ist eindeutig das Verhalten, nach dem diese Frage sucht. Wenn ich anrufe truncate(-3.14)und -4zurückerhalte, würde ich das definitiv als unerwünscht bezeichnen.
Nick Knowlson
Ich stimme Thomas zu. Der Unterschied in der Perspektive kann damit zusammenhängen, ob Sie normalerweise zur Anzeige oder zur Berechnung abschneiden. Aus rechnerischer Sicht werden dadurch "arithmetische Artefakte"
2
var a = 65.1 var truncated = Math.floor(a * 100) / 100; // = 65.09 Daher ist dies keine korrekte Lösung
Sanyam Jain
22

Sie können die Rundung korrigieren, indem Sie 0,5 für toFixed subtrahieren, z

(f - 0.005).toFixed(2)
RichardTheKiwi
quelle
1
Heads up: Dies funktioniert nicht für sehr kleine Zahlen, Zahlen mit mehr als 3 Dezimalstellen oder negative Zahlen. Versuchen Sie .0045, 5.4678 und -5.467
Nick Knowlson
Dies funktioniert so lange, wie Sie den Wert, den Sie subtrahieren, mit der gewünschten Länge abgleichen. Was auch immer Sie an toFixed () übergeben, muss die Anzahl der Nullen nach der Dezimalstelle sein.
Dmarra
18

Betrachten wir den Vorteil der doppelten Tilde unter:~~ .

Nimm die Nummer auf. Multipliziert mit signifikanten Stellen nach dem Komma , so dass Sie gestutzt mit Null Plätzen ~~. Teilen Sie diesen Multiplikator wieder heraus. Profitieren.

function truncator(numToTruncate, intDecimalPlaces) {    
    var numPower = Math.pow(10, intDecimalPlaces); // "numPowerConverter" might be better
    return ~~(numToTruncate * numPower)/numPower;
}

Ich versuche zu widerstehen, den ~~Anruf in Parens zu verpacken . Die Reihenfolge der Operationen sollte dafür sorgen, dass das richtig funktioniert, glaube ich.

alert(truncator(5.1231231, 1)); // is 5.1

alert(truncator(-5.73, 1)); // is -5.7

alert(truncator(-5.73, 0)); // is -5

JSFiddle-Link .

EDIT: Rückblickend habe ich unbeabsichtigt auch Fälle behandelt, um auch links von der Dezimalstelle abzurunden.

alert(truncator(4343.123, -2)); // gives 4300.

Die Logik ist ein wenig verrückt nach dieser Verwendung und kann von einem schnellen Refactor profitieren. Aber es funktioniert immer noch. Besser glücklich als gut.

Ruffin
quelle
Dies ist die beste Antwort. Wenn Sie den MathPrototyp damit erweitern und vor der Ausführung nach NaN-s suchen, ist dies einfach perfekt.
Bartłomiej Zalewski
truncator((10 * 2.9) / 100, 2)Rückgabe 0,28 statt 0,29 ... jsfiddle.net/25tgrzq1
Alex
14

Schöne einzeilige Lösung:

function truncate (num, places) {
  return Math.trunc(num * Math.pow(10, places)) / Math.pow(10, places);
}

Dann nenne es mit:

truncate(3.5636232, 2); // returns 3.56
truncate(5.4332312, 3); // returns 5.433
truncate(25.463214, 4); // returns 25.4632
MeestorHok
quelle
2
Ich mag diese Lösung, aber denke nur daran, dass sie nicht von allen Browsern vollständig unterstützt wird. ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
Gene Parcellano
11

Ich dachte, ich würde eine Antwort mit geben, |da es einfach ist und gut funktioniert.

truncate = function(number, places) {
  var shift = Math.pow(10, places);

  return ((number * shift) | 0) / shift;
};
Daniel X Moore
quelle
1
Guter Anruf. Wenn Sie einen bitweisen Operator verwenden, wird der Wert in ein int umgewandelt, und ormit 0 bedeutet "behalten Sie einfach das, was ich bereits habe". Tut was meine ~~Antwort macht, aber mit einer einzigen bitweisen Operation. Obwohl es die gleiche Einschränkung hat wie auch geschrieben: Wir können nicht über 2 ^ 31 gehen .
Ruffin
1
nicht korrekt, wenn truncate((10 * 2.9) / 100);dieser Code 0,28 statt 0,29 zurückgibt jsfiddle.net/9pf0732d
Alex
@Alex Wie ich vermute, stellen Sie fest ... Willkommen bei JavaScript! . Es gibt Korrekturen. Vielleicht möchten Sie eine teilen? : D
Ruffin
@ruffin Ich weiß über dieses Problem Bescheid =) Ich dachte, diese Antwort wäre die Lösung für dieses Problem. Leider habe ich noch keine genaue Lösung gefunden, überall gibt es ein solches Problem.
Alex
7

Mit bitweisen Operatoren abschneiden:

~~0.5 === 0
~~(-0.5) === 0
~~14.32794823 === 14
~~(-439.93) === -439
John Strickler
quelle
Wie funktioniert diese Referenz: stackoverflow.com/questions/7487977/…
jperelli
Wie schneidet es etwas auf beispielsweise 2 Dezimalstellen ab?
Salman A
7

@ Dogberts Antwort kann mit verbessert werden Math.trunc, was abschneidet anstatt zu runden.

Es gibt einen Unterschied zwischen Runden und Abschneiden. Abschneiden ist eindeutig das Verhalten, nach dem diese Frage sucht. Wenn ich Truncate (-3.14) anrufe und -4 zurückerhalte, würde ich das definitiv als unerwünscht bezeichnen. - @NickKnowlson

var a = 5.467;
var truncated = Math.trunc(a * 100) / 100; // = 5.46
var a = -5.467;
var truncated = Math.trunc(a * 100) / 100; // = -5.46
zurfyx
quelle
1
Das funktioniert nicht in allen Fällen, dh console.log (Math.trunc (9.28 * 100) / 100); // 9.27
Mike Makuch
@MikeMakuch das ist kein Problem Math.trunc, sondern eher als 9.28 * 100ist , 927.9999statt 928. Vielleicht möchten Sie über die Gefahren des Gleitkommas
zurfyx
4
Number.prototype.trim = function(decimals) {
    var s = this.toString();
    var d = s.split(".");
    d[1] = d[1].substring(0, decimals);
    return parseFloat(d.join("."));
}

console.log((5.676).trim(2)); //logs 5.67
Max Zlotskiy
quelle
Ich mag es, dass dies mit Strings funktioniert, wodurch die Nuancen von Gleitkommazahlen eliminiert werden. Vielen Dank!
iCode
4

Ich habe eine Antwort mit einer kürzeren Methode geschrieben. Folgendes habe ich mir ausgedacht

function truncate(value, precision) {
    var step = Math.pow(10, precision || 0);
    var temp = Math.trunc(step * value);

    return temp / step;
}

Die Methode kann so verwendet werden

truncate(132456.25456789, 5)); // Output: 132456.25456
truncate(132456.25456789, 3)); // Output: 132456.254
truncate(132456.25456789, 1)); // Output: 132456.2   
truncate(132456.25456789));    // Output: 132456

Wenn Sie eine kürzere Syntax wünschen, können Sie loslegen

function truncate(v, p) {
    var s = Math.pow(10, p || 0);
    return Math.trunc(s * v) / s;
}

quelle
4

Ich denke, diese Funktion könnte eine einfache Lösung sein:

function trunc(decimal,n=2){
  let x = decimal + ''; // string 
  return x.lastIndexOf('.')>=0?parseFloat(x.substr(0,x.lastIndexOf('.')+(n+1))):decimal; // You can use indexOf() instead of lastIndexOf()
}

console.log(trunc(-241.31234,2));
console.log(trunc(241.312,5));
console.log(trunc(-241.233));
console.log(trunc(241.2,0));  
console.log(trunc(241));

Julio Cesar Cervantes Martinez
quelle
Zwei Jahre nachdem dies veröffentlicht wurde, bin ich jedoch darauf gestoßen, als ich versuchte, den besten Weg mit Math.trunc, Regex usw. zu finden. Ich mag diese Lösung wirklich. Ganz einfach, funktioniert aber perfekt (für meinen Anwendungsfall sowieso).
Giles123
Vergessen Sie jedoch nicht, n = 0 zu berücksichtigen.
Giles123
3

Ich habe ein Problem gefunden: unter Berücksichtigung der nächsten Situation: 2.1 oder 1.2 oder -6.4

Was ist, wenn Sie immer 3 Dezimalstellen oder zwei oder was auch immer wollen, also müssen Sie die führenden Nullen rechts vervollständigen

// 3 decimals numbers
0.5 => 0.500

// 6 decimals
0.1 => 0.10000

// 4 decimales
-2.1 => -2.1000

// truncate to 3 decimals
3.11568 => 3.115

Dies ist die feste Funktion von Nick Knowlson

function truncateDecimals (num, digits) 
{
    var numS = num.toString();
    var decPos = numS.indexOf('.');
    var substrLength = decPos == -1 ? numS.length : 1 + decPos + digits;
    var trimmedResult = numS.substr(0, substrLength);
    var finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;

    // adds leading zeros to the right
    if (decPos != -1){
        var s = trimmedResult+"";
        decPos = s.indexOf('.');
        var decLength = s.length - decPos;

            while (decLength <= digits){
                s = s + "0";
                decPos = s.indexOf('.');
                decLength = s.length - decPos;
                substrLength = decPos == -1 ? s.length : 1 + decPos + digits;
            };
        finalResult = s;
    }
    return finalResult;
};

https://jsfiddle.net/huttn155/7/

Juanpscotto
quelle
x = 0.0000Test truncateDecimals (x, 2)schlägt fehl. kehrt zurück 0. nicht wie erwartet0.00
Ling Loeng
3
function toFixed(number, digits) {
    var reg_ex = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)")
    var array = number.toString().match(reg_ex);
    return array ? parseFloat(array[1]) : number.valueOf()
}

var test = 10.123456789
var __fixed = toFixed(test, 6)
console.log(__fixed)
// => 10.123456
Flavio
quelle
2

Hier ist eine einfache, aber funktionierende Funktion zum Abschneiden von Zahlen mit bis zu 2 Dezimalstellen.

           function truncateNumber(num) {
                var num1 = "";
                var num2 = "";
                var num1 = num.split('.')[0];
                num2 = num.split('.')[1];
                var decimalNum = num2.substring(0, 2);
                var strNum = num1 +"."+ decimalNum;
                var finalNum = parseFloat(strNum);
                return finalNum;
            }
RohannG
quelle
2

Der resultierende Typ bleibt eine Zahl ...

/* Return the truncation of n wrt base */
var trunc = function(n, base) {
    n = (n / base) | 0;
    return base * n;
};
var t = trunc(5.467, 0.01);
Bob Lyon
quelle
2

Lodash hat ein paar nützliche Methoden Math das kann rund , Boden und ceil eine Zahl zu einer bestimmten Dezimalgenauigkeit. Dies lässt nachgestellte Nullen aus.

Sie verfolgen einen interessanten Ansatz, indem sie den Exponenten einer Zahl verwenden. Anscheinend vermeidet dies Rundungsprobleme.

(Hinweis: funcist Math.roundoder ceiloder floorim Code unten)

// Shift with exponential notation to avoid floating-point issues.
var pair = (toString(number) + 'e').split('e'),
    value = func(pair[0] + 'e' + (+pair[1] + precision));

pair = (toString(value) + 'e').split('e');
return +(pair[0] + 'e' + (+pair[1] - precision));

Link zum Quellcode

Matthias
quelle
2

Die Antwort von @kirilloid scheint die richtige Antwort zu sein, der Hauptcode muss jedoch aktualisiert werden. Seine Lösung kümmert sich nicht um negative Zahlen (die jemand im Kommentarbereich erwähnt hat, aber im Hauptcode nicht aktualisiert wurde).

Aktualisierung auf eine vollständig endgültig getestete Lösung:

Number.prototype.toFixedDown = function(digits) {
    var re = new RegExp("([-]*\\d+\\.\\d{" + digits + "})(\\d)"),
    m = this.toString().match(re);
    return m ? parseFloat(m[1]) : this.valueOf();
};

Beispielnutzung:

var x = 3.1415629;
Logger.log(x.toFixedDown(2)); //or use whatever you use to log

Geige: JS-Nummer Abrunden

PS: Nicht genug Repo, um diese Lösung zu kommentieren.

ProgWiz
quelle
1

Hier meine Sicht auf das Thema:

convert.truncate = function(value, decimals) {
  decimals = (decimals === undefined ? 0 : decimals);
  return parseFloat((value-(0.5/Math.pow(10, decimals))).toFixed(decimals),10);
};

Es ist nur eine etwas ausgefeiltere Version von

(f - 0.005).toFixed(2)
opensas
quelle
1

Diejenige, die als Lösung markiert ist, ist die bessere Lösung, die ich bis heute gefunden habe, hat aber ein ernstes Problem mit 0 (zum Beispiel ergibt 0.toFixedDown (2) -0.01). Also schlage ich vor, dies zu verwenden:

Number.prototype.toFixedDown = function(digits) {
  if(this == 0) {
    return 0;
  }
  var n = this - Math.pow(10, -digits)/2;
  n += n / Math.pow(2, 53); // added 1360765523: 17.56.toFixedDown(2) === "17.56"
  return n.toFixed(digits);
}
Sebastián Rojas
quelle
1

Folgendes benutze ich:

var t = 1;
for (var i = 0; i < decimalPrecision; i++)
    t = t * 10;

var f = parseFloat(value);
return (Math.floor(f * t)) / t;
Steve
quelle
1
const TO_FIXED_MAX = 100;

function truncate(number, decimalsPrecison) {
  // make it a string with precision 1e-100
  number = number.toFixed(TO_FIXED_MAX);

  // chop off uneccessary digits
  const dotIndex = number.indexOf('.');
  number = number.substring(0, dotIndex + decimalsPrecison + 1);

  // back to a number data type (app specific)
  return Number.parseFloat(number);
}

// example
truncate(0.00000001999, 8);
0.00000001

arbeitet mit:

  • negative Zahlen
  • sehr kleine Zahlen (Number.EPSILON Genauigkeit)
givanse
quelle
0

Nur um auf eine einfache Lösung hinzuweisen, die für mich funktioniert hat

konvertiere es in einen String und regex es dann neu ...

var number = 123.45678;
var number_s = '' + number;
var number_truncated_s = number_s.match(/\d*\.\d{4}/)[0]
var number_truncated = parseFloat(number_truncated_s)

Es kann mit abgekürzt werden

var number_truncated = parseFloat(('' + 123.4568908).match(/\d*\.\d{4}/)[0])
Sergio Campamá
quelle
0

Hier ist ein ES6-Code, der macht, was Sie wollen

const truncateTo = (unRouned, nrOfDecimals = 2) => {
      const parts = String(unRouned).split(".");

      if (parts.length !== 2) {
          // without any decimal part
        return unRouned;
      }

      const newDecimals = parts[1].slice(0, nrOfDecimals),
        newString = `${parts[0]}.${newDecimals}`;

      return Number(newString);
    };

// your examples 

 console.log(truncateTo(5.467)); // ---> 5.46

 console.log(truncateTo(985.943)); // ---> 985.94

// other examples 

 console.log(truncateTo(5)); // ---> 5

 console.log(truncateTo(-5)); // ---> -5

 console.log(truncateTo(-985.943)); // ---> -985.94

Cristian Sima
quelle
0
Number.prototype.truncate = function(places) {
  var shift = Math.pow(10, places);

  return Math.trunc(this * shift) / shift;
};
Dercni
quelle
0

Sie können mit Zeichenfolgen arbeiten. Es prüft, ob '.' existiert und entfernt dann einen Teil der Zeichenfolge.

abschneiden (7,88, 1) -> 7,8

abschneiden (7.889, 2) -> 7.89

abschneiden (-7,88, 1) -> -7,88

function  truncate(number, decimals) {
    const tmp = number + '';
    if (tmp.indexOf('.') > -1) {
        return +tmp.substr(0 , tmp.indexOf('.') + decimals+1 );
    } else {
        return +number
    }
 }
Daniel Alejandro Lima Monzon
quelle