Esta é uma pequena amostra do que tenho em meu banco de dados.
CREATE TEMPORARY TABLE temp_ledger(
book_entry INT,
credit DOUBLE(24, 8),
debit DOUBLE(24, 8)
);
INSERT INTO
temp_ledger(book_entry, credit, debit)
VALUES
(1, 17.54500000, 0.00000000),
(1, 0.00000000, 14.50000000),
(1, 0.00000000, 3.04500000),
(2, 0.00000000, 99.85500000),
(2, 95.10000000, 0.00000000),
(2, 4.75500000, 0.00000000);
SELECT
book_entry,
SUM(ROUND(debit, 2)) as sum_of_rounded_debit,
SUM(ROUND(credit, 2)) as sum_of_rounded_credit,
ROUND(SUM(debit), 2) as round_of_summed_debit,
ROUND(SUM(credit), 2) as round_of_summed_credit,
SUM(debit) as summed_debit,
SUM(credit) as summed_credit
FROM
temp_ledger
GROUP BY
book_entry;
DROP TEMPORARY TABLE temp_ledger;
Saída
temp_ledger
entrada_livro | soma_de_débito_arredondado | soma_de_crédito_arredondado | round_of_summed_debit | round_of_summed_credit | débito_somado | crédito_sumado |
---|---|---|---|---|---|---|
1 | 17,54 | 17,55 | 17,55 | 17,55 | 17.54500000 | 17.54500000 |
2 | 99,86 | 99,86 | 99,86 | 99,85 | 99.85500000 | 99.85500000 |
Não entendo por que sum_of_rounded_debit
a entrada do livro 1 é 17,54 :
- 14,50 arredondado deve ser 14,50
- 3,045 arredondado deve ser 3,05
E round_of_summed_credit
para o lançamento contábil 2 é 99,85 se summed_credit for 99,855 , por que o arredondamento me dá 99,85 e não 99,86
Eu entendo que às vezes os decimais podem ser complicados, mas round_of_summed_credit
em particular me pergunto "O quê?".
Isso surgiu ao tentar encontrar erros na tabela llx_accounting_bookkeeping de Dolibarr, uma vez que a soma da rodada fornece algumas linhas como incompatíveis entre crédito e débito, enquanto a soma da rodada fornece um conjunto diferente de linhas.
Usando MariaDB 10.5.8
Os números de ponto flutuante funcionam na base 2, portanto a conversão de e para a base 10 sempre introduz pequenos erros. Quando eles são convertidos para base 10 para exibição, isso é (um pouco e geralmente, mas nem sempre) oculto pelo arredondamento para menos que o número total de dígitos... mas se você espera que o ponto flutuante seja exato, não funcionará.
Por exemplo, o número "3.045" não pode ser codificado como precisão dupla de ponto flutuante, portanto, a representação mais próxima disponível é usada:
3.04499999999999992895
Ao usar round() com número de decimais 2, ele é arredondado para:
3.0400000000000003553
Este resultado está correto, porque 3,044999... (que arredonda para 3,04) não é 3,045 (que arredonda para 3,05). O problema é que você acredita que converter 3,045 para ponto flutuante de precisão dupla dá um resultado igual a 3,045. Isso não.
Python demonstra:
A natureza não exata dos números flutuantes significa que eles nunca devem ser usados para representar números exatos de base 10, especialmente no contexto da contabilidade.
Felizmente este é um problema resolvido, pois seu banco de dados fornece um tipo exato de ponto fixo: Numeric/Decimal .
Você pode especificar a precisão desses tipos decimais conforme desejar. Normalmente na contabilidade a menor unidade é o centavo, não é possível fazer transferência bancária ou cheque com centavos fracionários, portanto os números são especificados com precisão de 2 dígitos após a vírgula. Em outros contextos, como criptografia, onde um azarado proprietário de $ DOGE vende, você poderia ter uma transferência de 0,000002 BTC, então você pode querer adicionar mais dígitos.
A questão toda é que o valor armazenado na tabela é a quantia exata de dinheiro que foi transferida na transação, e não uma conversão aproximada para float.
Feito isso, todos os cálculos estarão corretos.
Não tenho ideia do que sum(round()) deve alcançar. Se você quiser sum() de pagamentos, então você deve fazer sum() de pagamentos, e não arredondá-los antes de somar.
Há uma explicação possível: os designers deste banco de dados usaram floats para contabilidade , o que significa que eles não têm ideia do que estão fazendo. Então eles usaram round() como uma tentativa de corrigir os erros introduzidos pelo uso do tipo de dados errado.
Não vai funcionar. Você deve usar o tipo de dados correto ou seus resultados sempre estarão errados.
Observe que, como os dados na tabela estão no tipo de dados errado, a conversão para Decimal pode apresentar problemas. Por exemplo, se o valor "3,045" (na verdade 3,04499999999999992895) estiver armazenado e você o converter para Decimal(8,2), verifique se ele fornece o resultado desejado.
Como todo o aplicativo é escrito em php, e o php não tem tipo Decimal (apenas flutuantes)... a maneira usual de fazer isso em php é converter tudo em centavos e usar apenas números inteiros. Portanto, os cálculos feitos no aplicativo também podem estar errados . Os sintomas incluem somas calculadas em php diferentes das somas calculadas no banco de dados.