The following code excerpt will add up a column of numbers and yield results that are several decimal points long. Could anyone please give an example of how to convert the results into dollars and cents, rounding to the second decimal -- i.e., 1.555
should round up to 1.56
; and 1.554
should round down to 1.55
. Also, I'd like to insert comma separators every three digits to the left of the decimal point -- e.g., 1124412.555
should be converted to 1,124,412.56
.
(let ((sum 0))
(while (re-search-forward "[0-9]*\\.?[0-9]+" nil t)
(setq sum (+ sum (string-to-number (match-string 0)))))
(insert "\n\nTotal Hours: " (format "%s" sum ))
(insert "\n\nTotal Fee for Services Rendered: " (format "%s" (* 250 sum)))
(insert "\n\nOne-third of total fee: " (format "%s" (/ (* 250 sum) 3))))
2.0
0.2
0.1
4.75
4.0
6.5
0.1
Total Hours: 17.650000000000002
Total Fee for Services Rendered: 4412.500000000001
One-third of total fee: 1470.8333333333337
Based upon the answers provided by @Drew and @abo-abo, the following is a revised draft that now appears to be working correctly:
08884.75585
78774.1235
6.545
Total Hours: 87,665.42
Total Fee: $21,916,356.09
One-Third: $7,305,452.03
(let ((sum 0))
(while (re-search-forward "[0-9]*\\.?[0-9]+" nil t)
(setq sum (+ sum (string-to-number (match-string 0)))))
(setq total-hours (group-number (number-conversion (format "%s" sum ))))
(setq services-rendered (group-number (number-conversion (format "%s" (* 250 sum)))))
(setq one-third (group-number (number-conversion (format "%s" (/ (* 250 sum) 3)))))
(insert "\n\nTotal Hours: " total-hours)
(insert "\n\nTotal Fee: $" services-rendered)
(insert "\n\nOne-Third: $" one-third) )
;; @abo-abo
(defun number-conversion (str)
(let ((x (read str)))
(format "%0.2f" (* 0.01 (round (* 100 x)))) ))
;; http://www.emacswiki.org/emacs/ElispCookbook#toc23
(defun group-number (num &optional size char)
"Format NUM as string grouped to SIZE with CHAR."
;; Based on code for `math-group-float' in calc-ext.el
(let* ((size (or size 3))
(char (or char ","))
(str (if (stringp num)
num
(number-to-string num)))
(pt (or (string-match "[^0-9a-zA-Z]" str) (length str))))
(while (> pt size)
(setq str (concat (substring str 0 (- pt size))
char
(substring str (- pt size)))
pt (- pt size)))
str))
I believe abo-abo's (* 0.01 (round (* 100 x)))
can lead to surprises in corner cases (because 0.01 cannot be represented exactly in the internal floating point representation). A simpler solution for the rounding is:
(format "%0.2f" x)
Use (format "%.2f" YOUR-NUMBER)
. For example:
(insert "\n\nOne-third of total fee: " (format "%.2f" (/ (* 250 sum) 3))))
The f
says to use decimal-point notation. The .2
says to show two decimal places. See the doc string of format
or the Elisp manual, node Formatting Strings
.
As far as I know, in Emacs Lisp there is no predefined format specifier that gives you comma-separated digit grouping. In Common Lisp format
is much more powerful (a language by itself).
If you really need comma separation then you will need to write code that parses the text (e.g. the string returned from format
) and insert commas where needed. Perhaps someone else will bother to do that for you here, but you can probably do it yourself, given the info you have so far. There is nothing predefined that will help magically with this.
But this might help with the commas.
Here's the code:
(defun num->dollars (str)
(let ((x (read str)))
(replace-regexp-in-string
"\\."
","
(format "%0.2f"
(* 0.01 (round (* 100 x)))))))
(mapcar
#'num->dollars
'("4412.500000000001" "1470.8333333333337" "1.555" "1.554"))
; => ("4412,50" "1470,83" "1,56" "1,55")
In general, it's worth considering the built-in calc
library when dealing with money values (or other decimal fractions where precision is critical).
calc
uses a decimal representation for floating-point numbers (rather than binary), so is not subject to the classical float rounding issues when dealing with decimal values.
It can also take care of grouping digits for display.
See C-hig (calc)
RET
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With