Newsgroup: it.comp.www.php
Titolo: arrotondare importo

Dunque...
da una query mi calcolo l'imponibile. La cifra è con due decimali (es.
30,38)
Però c'è un problema di arrotondamento.
La cifra esatta sarebbe 30,3890 e quindi dovrebbe scrivermi 30,39 anzichè
30,38.
Mi aiutate?
Grazie
# Calcolo IVA
$sql1 = "SELECT (SUM(importo)-SUM(importo)/(12/10)) FROM tabella WHERE id =
'$id'";
$rs1 = mysql_query($sql1, $cid);
if (mysql_error()) { print "Database Error: $sql " . mysql_error(); }
$iva1=mysql_result($rs1,0,0);
$iva= number_format ( $iva1, 2, ',', ' ');
# Calcolo Imponibile
$sql2 = "SELECT (SUM(importo)/(12/10)) FROM tabella WHERE id = '$id'";
$rs2 = mysql_query($sql2, $cid);
if (mysql_error()) { print "Database Error: $sql " . mysql_error(); }
$imp1=mysql_result($rs2,0,0);
$imp= number_format ( $imp1, 2, ',', ' ');

 Grazie
ciao ..
-- 
#include <stdio.h>
int main(void){char c[]={10,65,110,116,111,110,105,111,32,98,97,114,98,111,110,
101,32,60,104,105,110,100,101,109,105,116,64,116,105,115,99,97,108,105,110,101,
116,46,105,116,62,10,10,0};printf("%s",c);return 0;}

 
Non so se può essere utile, ma ti posto un messaggio apparso qui sul NG 
che mi ero messo da parte come 'future reference'.
L'autore originale è Umberto Salsi (diamo a Cesare...) se cerchi su 
Google dovresti tovare il tread completo
Ciao
--8<--------8<--------8<--------8<--------8<--------8<--------8<--------8<------
Far di conto in Euro col PHP
----------------------------
Far di conto con gli Euro sembra facile, ma non lo e'.
Le soluzioni che presento qui sono il risultato di un fruttuoso scambio
di idee avvenuto sul newsgroup it.comp.www.php. Commenti, critiche,
precisazioni, soluzioni alternative, non solo sono benvenute, ma
necessarie.
Il problema
-----------
Un bel giorno mi e' capitato di fare questo semplice calcolo: la differenza
tra 57 centesimi e 56 centesimi fa un centesimo, giusto? sbagliato!
     echo(0.57-0.56); ==> 0.0099999999999999
Dopo un attimo di sconcerto, mi sono accorto che in realta' non c'e'
nulla di strano in questo risultato: PHP ha convertito i due numeri
decimali in float, ha eseguito la differenza e ha mostrato il risultato,
approssimato nei limiti di precisione dei float (in effetti il risultato
e' mooooolto vicino a quel 0.01 che avrei voluto vedere). Se l'espressione
aritmetica fosse stata appena piu' complicata, avremmo ottenuto risultati
ancora piu' sorprendenti.
Il problema sta nel fatto che certi numeri frazionari che espressi in
base dieci hanno un numero finito di cifre decimali, in base due non
hanno una rappresentazione finita di cifre binarie: in altri termini,
ogni volta che convertiamo un numero decimale frazionario in un float
abbiamo una PERDITA IRREVERSIBILE DI INFORMAZIONE.
La morale: non e' possibile eseguire calcoli monetari in Euro usando i
float di PHP, o almeno non e' possibile farlo senza alcuni accorgimenti.
Nelle discussioni su questo newsgroup avvenute qualche tempo fa, grazie
al contributo di vari partecipanti, siamo pervenuti a due soluzioni
possibili:
Soluzione 1: usando la libreria BC Math
---------------------------------------
Esiste un porting su PHP della famosa libreria di calcolo matematico
in precisione arbitraria che si chiama BC Math. Essenzialmente,
questa libreria rappresenta i numeri come strighe di cifre decimali,
e permette di definire un numero fisso di decimali coi quali eseguire
i calcoli. Siccome in tal caso non ha mai luogo la nefasta conversione
decimale -> binario, non troveremo risultati strani.
Visto che in questo caso i numeri sono stringhe, dovremo eseguire TUTTI
i calcoli sfruttando le apposite funzioni della libreria: bcadd() per
eseguire le somme, bcmul() per eseguire le moltiplicazioni, bccomp()
per confrontare valori, ecc. C'e' poi anche la funzione bcscale()
che permette di impostare il numero di decimali da usare: due decimali
potrebbero bastare per i calcoli in Euro, ma alcuni riferimenti normativi
raccomandano di usare 5 decimali per i calcoli intermedi, arrotondando
poi il risultato finale a due decimali. Ad esempio:
     $imponibile = "1234.56";
     # calcola l'IVA 20% (e' un calcolo intermedio: uso 5 decimali):
     bcscale(5);
     $iva = bcadd( bcmul($imponibile, "0.20"), "0.004" );
     # tronca i decimali dal terzo in poi:
     bcscale(2);
     $iva = bcadd($iva, "0");
     # calcola il totale (bisogna usare i valori a due decimali):
     $totale = bcadd($imponibile, $iva);
     # stampa la fattura:
     echo("IMPONIBILE = $imponibile\n");
     echo("IVA 20% = $iva\n");
     echo("TOTALE FATTURA = $totale\n");
Nel DB dovremo salvare l'imponibile, l'iva e il totale con due decimali.
I vari DB conformi ANSI/ISO SQL92 hanno un tipo dedicato per i valori
monetari. Ad esempio, in PostgreSQL e in MySQL potremo usare:
     numeric(11,2)
che rappresenta importi fino a 1 miliardo di Euro, oltre alle due cifre
decimali. Al livello del DB la rappresentazione di questi numeri e' come
stringhe di caratteri, per cui quando si vanno a leggere e scrivere con
il PHP vanno trattati come stringhe.
Soluzione 2: usando i float del PHP e facendo i conti in centesimi
------------------------------------------------------------------
Due osservazioni:
- il manuale del PHP assicura che i numeri float offrono almeno 14 cifre
significative in tutte le implementazioni di questo linguaggio;
- fintanto che i numeri da rappresentare sono interi, non avviene la
nefasta conversione tra numeri frazionari tra la rappresentazione dei
numeri frazionari in base 10 (quella che usiamo noi) e la base 2 (quella
che usa il computer), e quindi non si ha perdita di precisione.
Quindi, ad esempio, l'imponibile di 1234,56 Euro lo possiamo rappresentare
come 123456 Centesimi: in questo caso il PHP non sta neppure a scomodare
la precisione float, e usa dei semplici numeri interi (possiamo contare
su almeno 32 bit con segno). Per importi superiori ai 20 milioni di Euro,
PHP attiva la rappresentazione float che ci permette fino a 14 cifre,
con le quali possiamo rappresentare fino a 1 miliardo di Euro (Euro piu',
Euro meno).
Difetto 1: per numeri superiori a 1 miliardo di Euro si attiva la
notazione scientifica dei float, e si perdono alcune cifre significative
(saltano prima i centesimi, poi i decimi di Euro, poi gli Euro, poi le
decine di Euro, ecc.).
Difetto 2: la precisione dei calcoli intermedi e' fissata dal numero
di cifre significative dei float, e quindi non puo' essere controllata;
rimane comunque una precisione elevatissima.
Dunque, riassumiamo: nel PHP e nel DB spariscono gli Euro, e ci sono
solo Centesimi; solo a livello della interfaccia utente avremo cura di
presentare gli importi monetari posizionando la virgola come si conviene.
Per eseguire queste piccole conversioni Euro <--> Centesimi ho scritto
alcune funzioncine apposite.
Vediamo come si presenta il calcolo della fattura di prima:
     $imponibile = 123456;
     $iva = floor( $imponibile * 0.20 + 0.004 );
     $totale = $imponibile + $iva;
     # stampa la fattura:
     echo("IMPONIBILE = $imponibile Centesimi di Euro\n");
     echo("IVA 20% = $iva Centesimi di Euro\n");
     echo("TOTALE FATTURA = $totale Centesimi di Euro\n");
Se gli importi monetari espressi in centesimi di Euro non vi piacciono
basta convertirli in Euro con una funzioncina come questa:
     function Cent2Euro($c)
     {
         $intero = floor( $c / 100.0 );
         $frazione = $c - 100.0 * $intero;
         if( $frazione <= 9 )  $frazione = "0$frazione";
         return "$intero,$frazione";
     }
Per la validazione dell'input dell'utente scriveremo un'altra
funzioncina, e per la conversione del valore inserito dall'utente
(espresso in Euro) nella nostra rappresentazione in centesimi useremo
un'altra funzioncina ancora. Queste tre funzioni ci permetteranno di
astrarre tra la rappresentazione interna del programma (in Centesimi)
e la rappresentazione a livello della interfaccia utente (in Euro).
I DB supportano tipi interi in precisione multipla, capaci di
rappresentare facilmente numeri interi a 64 bit con segno (bigint o int8
per PostgreSQL e MySQL) che occupano solo 8 Byte, ci possono stare fino a
92 milioni di miliardi di Euro e sono molto efficienti da gestire perche'
i numeri sono rappresentati in forma binaria.
Ringraziamenti
--------------
Grazie a Jurgen Schwietering e a Marco Natoni per avere suggerito queste
soluzioni. Io avevo posto solo il problema!
Ciao,
  ___
/_|_\  Umberto Salsi
\/_\/  
www.icosaedro.it