1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-14 15:40:19 +02:00

i18n: Fix corner cases for monetary and number string conversions.

Fixes <http://bugs.gnu.org/24990>.
Reported by Martin Michel <dev@famic.de>.

* module/ice-9/i18n.scm (integer->string, number-decimal-string): New
procedures.
(monetary-amount->locale-string): Use them instead of 'number->string'
followed by 'string-split'.
(number->locale-string): Likewise.
* test-suite/tests/i18n.test ("number->locale-string")["fraction"]: Add
second argument to 'number->locale-string'.
["fraction, 1 digit"]: Round up.
["fraction, 10 digits", "trailing zeros", "negative integer"]: New
tests.
* test-suite/tests/i18n.test ("format ~h"): Pass the number of decimals
for ~h.
("monetary-amount->locale-string")["French"]: Always expect two decimals
after the comma.
["one cent", "very little money"]: New tests.
* test-suite/tests/format.test ("~h localized number")["1234.5"]:
Specify the number of decimals explicitly.
["padding"]: Expect zero decimals.
["padchar"]: Ask for one decimal.
["decimals", "locale"]: Adjust rounding.
This commit is contained in:
Ludovic Courtès 2017-02-13 00:07:40 +01:00
parent 21602109f0
commit 4aead68cdb
3 changed files with 88 additions and 30 deletions

View file

@ -246,6 +246,36 @@
'unspecified 'unspecified)
(define (integer->string number)
"Return a string representing NUMBER, an integer, written in base 10."
(define (digit->char digit)
(integer->char (+ digit (char->integer #\0))))
(if (zero? number)
"0"
(let loop ((number number)
(digits '()))
(if (zero? number)
(list->string digits)
(loop (quotient number 10)
(cons (digit->char (modulo number 10))
digits))))))
(define (number-decimal-string number digit-count)
"Return a string representing the decimal part of NUMBER, with exactly
DIGIT-COUNT digits"
(if (integer? number)
(make-string digit-count #\0)
;; XXX: This is brute-force and could be improved by following one
;; of the "Printing Floating-Point Numbers Quickly and Accurately"
;; papers.
(let ((number (* (expt 10 digit-count)
(- number (floor number)))))
(string-pad (integer->string (round (inexact->exact number)))
digit-count
#\0))))
(define (%number-integer-part int grouping separator)
;; Process INT (a string denoting a number's integer part) and return a new
;; string with digit grouping and separators according to GROUPING (a list,
@ -336,12 +366,11 @@ locale is used."
(substring dec 0 fraction-digits)
dec)))))
(external-repr (number->string (if (>= amount 0) amount (- amount))))
(int+dec (string-split external-repr #\.))
(int (car int+dec))
(dec (decimal-part (if (null? (cdr int+dec))
""
(cadr int+dec))))
(int (integer->string (inexact->exact
(floor (abs amount)))))
(dec (decimal-part
(number-decimal-string (abs amount)
fraction-digits)))
(grouping (locale-monetary-digit-grouping locale))
(separator (locale-monetary-thousands-separator locale)))
@ -388,14 +417,14 @@ number of fractional digits to be displayed."
(substring dec 0 fraction-digits)
dec))))))
(let* ((external-repr (number->string (if (>= number 0)
number
(- number))))
(int+dec (string-split external-repr #\.))
(int (car int+dec))
(dec (decimal-part (if (null? (cdr int+dec))
""
(cadr int+dec))))
(let* ((int (integer->string (inexact->exact
(floor (abs number)))))
(dec (decimal-part
(number-decimal-string (abs number)
(if (integer?
fraction-digits)
fraction-digits
0))))
(grouping (locale-digit-grouping locale))
(separator (locale-thousands-separator locale)))