Parayı ondalık bir sütunda saklama - ne hassasiyet ve ölçek?


173

Para değerlerini bir veritabanında saklamak için ondalık bir sütun kullanıyorum ve bugün hangi hassasiyeti ve ölçeği kullanacağımı merak ediyordum.

Sabit genişlikteki char sütunları daha verimli olduğu için, ondalık sütunlar için de aynı olabileceğini düşünüyordum. Bu mu?

Hangi hassasiyeti ve ölçeği kullanmalıyım? 7/24 hassasiyet düşünüyordum. Bu aşırı mı, yeterli değil mi?


Bunu yapmaya karar verdim:

  • Dönüşüm oranlarını (uygun olduğunda) işlem tablosunun kendisinde bir kayan nokta olarak depola
  • Para birimini hesap tablosunda saklama
  • İşlem tutarı DECIMAL(19,4)
  • Dönüşüm oranı kullanan tüm hesaplamalar uygulamam tarafından işlenecek, böylece yuvarlama sorunlarının kontrolünü elimde tutuyorum

Dönüşüm oranı için bir şamandıra bir sorun olduğunu düşünmüyorum, çünkü çoğunlukla referans amaçlıdır ve yine de ondalık sayıya çevireceğim.

Değerli girdiniz için hepinize teşekkür ederim.


2
Kendinize şu soruyu sorun: Verileri ondalık formda saklamak gerçekten gerekli mi? Verileri Sent / Pennies -> tamsayıları olarak saklayamıyorum?
Terence

5
DECIMAL(19, 4) popüler bir seçim olduğunu kontrol bu da kontrol buraya kullanımına kaç ondalık basamak karar vermek Dünya Para Biçimleri, umut yardımcı olur.
shaijut

Yanıtlar:


181

Her şeye uyan bir beden arıyorsanız, DECIMAL(19, 4)popüler bir seçim olduğunu öneririm (hızlı bir Google bunu taşır). Bunun eski VBA / Access / Jet Para Birimi veri türünden kaynaklandığını, dilde ilk sabit nokta ondalık türü olduğunu düşünüyorum; Decimalsadece VB6 / VBA6 / Jet 4.0'da 'sürüm 1.0' tarzında (yani tam olarak uygulanmadı) geldi.

Sabit nokta ondalık değerlerinin saklanması için temel kural, yuvarlama için izin vermeniz gerekenden en az bir ondalık basamak daha saklamaktır. Eski eşlenmesi için nedenlerden biri Currency, ön ucunda tipi DECIMAL(19, 4)arka uç tip oldu Currencyise, doğal olarak yuvarlama Sergilenen bankacılar DECIMAL(p, s)yuvarlak kesilmesiyle.

Depolama için fazladan bir ondalık yer DECIMAL, satıcının varsayılan değerini almak yerine özel bir yuvarlama algoritmasının uygulanmasına izin verir (ve bankacıların yuvarlaması, en azından söylemek gerekirse, .5 ile biten tüm değerlerin sıfırdan yuvarlanmasını bekleyen bir tasarımcı için endişe vericidir) .

Evet, DECIMAL(24, 8)bana kulak misafiri gibi geliyor. Çoğu para birimi dört veya beş ondalık basamağa aktarılır. Ben 8 ondalık ölçeği (veya daha fazla) durumlardan biliyor edilir gerekli ama 'normal' parasal tutarı (dört ondalık basamağı demek) buna göre azaltılmalıdır ondalık hassasiyeti ima yanlısı rata'd olmuştur budur (ayrıca dikkate bu gibi durumlarda kayan nokta tipi). Ve bugünlerde hiç kimse 24 ondalık bir hassasiyet gerektirecek kadar paraya sahip değil :)

Ancak, herkese uyan tek bir yaklaşımdan ziyade, bazı araştırmalar yapılabilir. Tasarımcınıza veya alan adı uzmanınıza uygulanabilecek muhasebe kurallarını sorun: GAAP, AB, vb. Beş ondalık basamağa yuvarlama için açık kurallarla bazı AB eyalet içi transferlerini belirsiz bir şekilde hatırlıyorum, bu nedenle DECIMAL(p, 6)depolama için kullanıyorum . Muhasebeciler genellikle dört ondalık basamağı tercih ederler.


PS SQL Server'ın MONEYveri türünden kaçının çünkü yuvarlanırken, taşınabilirlik vb. Gibi diğer hususların yanı sıra doğrulukla ilgili ciddi sorunlara sahiptir. Aaron Bertrand'ın bloguna bakın .


Microsoft ve dil tasarımcıları bankacının yuvarlamasını seçtiler, çünkü donanım tasarımcıları onu seçti [alıntı?]. Örneğin Elektrik ve Elektronik Mühendisleri Enstitüsü (IEEE) standartlarında yer almaktadır. Ve donanım tasarımcıları bunu seçti çünkü matematikçiler tercih etti. Wikipedia'ya bakınız ; Açıklama: Olasılık ve Hatalar Teorisi'nin 1906 baskısı buna 'bilgisayarın kuralı' ("bilgisayarlar", hesaplama yapan insanlar anlamına gelir) adını verdi.


1
Dil tasarımcılarının neden Banker'in yuvarlamasını seçtiği hakkında daha fazla bilgi için bu yanıta ve bu sayfaya bakın .
Nick Chammas

1
onedaywhen: Banker'in yuvarlanması Microsoft'a bağlı değil. @NickChammas: ne bir dil tasarımcısı buluşu ne de. Microsoft ve dil tasarımcıları temel olarak seçtiler çünkü donanım tasarımcıları bunu seçti; örneğin Elektrik ve Elektronik Mühendisleri Enstitüsü (IEEE) standartlarında yer almaktadır. Ve donanım tasarımcıları bunu seçti çünkü matematikçiler tercih etti. Bkz. En.wikipedia.org/wiki/Rounding#History ; Açıklama: Olasılık ve Hatalar Teorisi'nin 1906 baskısı buna 'bilgisayarın kuralı' ("bilgisayarlar", hesaplama yapan insanlar anlamına gelir) adını verdi.
phoog

9
peki Bitcoin için ne kullanmalıyım?
Alet

1
@onedayneden neden DECIMAL(19, 4)daha popüler DECIMAL(19, 2)? Çoğu dünya para birimi sadece iki ondalık basamaktır.
cokedude

3
@ zypA13510: evet, benim iddiam on yıl önce! Ama bu benim en çok cevap için üçüncü oy ve SO temsilcisi açısından adil bir değişiklik yığını için hesaplar, bu yüzden ben quids :)
onedaywhen

105

Kısa bir süre önce, birden fazla para biriminde değerleri ele alması ve bunlar arasında dönüşüm gerçekleştirmesi gereken bir sistem uyguladık ve birkaç şeyi zor yoldan çözdük.

PARA İÇİN ASLA YÜZER NOKTALARI KULLANMAYIN

Kayan nokta aritmetiği, bir şeyleri mahvedinceye kadar fark edilmeyebilecek yanlışlıklar getirir. Tüm değerler tamsayı veya sabit ondalık tür olarak depolanmalıdır ve sabit ondalık tür kullanmayı seçerseniz, bu türün başlık altında ne yaptığını tam olarak anladığınızdan emin olun (yani dahili olarak bir tamsayı veya kayan nokta kullanıyor mu? ) kullanımı önerilir.

Hesaplamalar veya dönüşümler yapmanız gerektiğinde:

  1. Değerleri kayan noktaya dönüştürme
  2. Yeni değeri hesapla
  3. Sayıyı yuvarlayın ve tekrar tamsayıya dönüştürün

Bir kayan nokta sayısını 3. adımda tekrar bir tam sayıya dönüştürürken, sadece dökmeyin - önce yuvarlamak için bir matematik işlevi kullanın. Bu genellikle round, özel durumlarda floorveya olsa da, olacaktır ceil. Farkı öğrenin ve dikkatlice seçin.

Bir sayının türünü değerin yanında sakla

Yalnızca bir para birimini işliyorsanız bu sizin için önemli olmayabilir, ancak birden fazla para birimini ele almamız bizim için önemliydi. USD, GBP, JPY, EUR gibi bir para birimi için 3 karakterlik kodu kullandık.

Duruma bağlı olarak, saklamak da yardımcı olabilir:

  • Sayının vergiden önce mi yoksa sonra mı olduğu (ve vergi oranının ne olduğu)
  • Sayının bir dönüşümün sonucu olup olmadığı (ve dönüştürüldüğünden)

Karşılaştığınız sayıların doğruluk sınırlarını öğrenin

Gerçek değerler için, para biriminin en küçük birimi kadar kesin olmak istersiniz. Bu, bir kuruş, bir kuruş, bir yen, bir fen, vs.'den daha küçük değerleriniz olmadığı anlamına gelir.

Dahili olarak, daha küçük değerlerle başa çıkmayı seçebilirsiniz, bu durumda farklı bir para birimi değeri türüdür . Kodunuzun hangisinin hangisi olduğunu bilmediğinden emin olun. Burada bile kayan nokta değerleri kullanmaktan kaçının.


Tüm bu kuralları bir araya getirerek aşağıdaki kurallara karar verdik. Çalışan kodda, para birimleri en küçük birim için bir tamsayı kullanılarak saklanır.

class Currency {
   String code;       //  eg "USD"
   int value;         //  eg 2500
   boolean converted;
}

class Price {
   Currency grossValue;
   Currency netValue;
   Tax taxRate;
}

Veritabanında, değerler aşağıdaki biçimde bir dize olarak saklanır:

USD:2500

25,00 dolarlık değeri depolar. Bunu sadece para birimleriyle ilgilenen kodun veritabanı katmanının içinde olması gerekmediği için yapabildik, böylece tüm değerler önce belleğe dönüştürülebilir. Diğer durumlar şüphesiz kendilerini diğer çözümlere borçlandıracaktır.


Daha önce açıklığa kavuşturamazsam, şamandıra kullanmayın!


1
Asla asla deme: bazen para miktarları orantılıdır ve daha sonra tekrar toplanması gerekir. Örnek: Toplam temettü miktarını (nispeten küçük) hisse başına net vermek üzere söz konusu hisse adedi (nispeten küçük) ile bölünmüştür. Bazen daha iyi yüzer :)
oneday22

23
Asla yanımda değilim. Kayan nokta spec, yaptığınız daha fazla hesaplamayı toplayacak yanlışlıklara sahiptir. Bir kuruş veya kuruştan daha küçük değerleri saklamanız gerekiyorsa, ihtiyacınız olan doğruluk seviyesini tanımlayın ve buna sadık kalın. Şamandıra kullanmayın. Ciddi anlamda. Bu kötü bir fikir.
Marcus Downing

4
Bu cevap ayrıca, Douglas Crockford'un "JavaScript serisinde Crockford" dizisinde özetlediği javascript en iyi uygulamalarıyla da örtüşüyor. Dolayısıyla, javascript'te para birimleriyle çalışıyorsanız, değeri bu şekilde saklamak çok mantıklıdır.
paperreduction

1
@onedayWhen hisse başına net vakalar, orantısal tutarları toplayabilir ve orijinal toplamla karşılaştırabilir ve kalanlarla başa çıkmak için bir strateji tasarlayabilir (ondalık / tamsayı türlerini kullanırken).
Shiv

2
Mysql için, bigintmiktarına göre sıralama yapmayı düşünüyorsanız , tamsayıyı (2500) bir olarak depolamanızı öneririm . Ve büyük tamsayılarla uğraşırken PHP 32bit ile zamanınızı boşa harcamayın, 64bit veya Node.JS'ye yükseltin;)
Ricky Boyce

4

MySQL'de para tutarken, para değerlerinizin doğruluğunu biliyorsanız DECIMAL (13,2) kullanın ya da yeterince hızlı yaklaşık bir değer istiyorsanız DOUBLE kullanın. Dolayısıyla, başvurunuzun bir trilyon dolara (veya avro veya pound) kadar para değerlerini işlemesi gerekiyorsa, bu işe yaramalıdır:

DECIMAL(13, 2)

Veya GAAP ile uyumlu olmanız gerekiyorsa şunu kullanın:

DECIMAL(13, 4)

3
2500 sayfalık bir dokümanın içeriği yerine GAAP yönergelerinin belirli bir bölümüne bağlanabilir misiniz ? Teşekkürler.
ReactingToAngularVues

@ReactingToAngularVues, ​​sayfanın değiştiği görülüyor. Üzgünüz
pollux1er

2

4 ondalık basamak size dünyanın en küçük para birimi alt birimlerini saklama hassasiyeti verir. Mikro ödeme (nanopayment ?!) doğruluğuna ihtiyacınız varsa daha da düşürebilirsiniz.

Ben de DECIMALDBMS'ye özgü para türlerini tercih ediyorum , bu tür bir mantığı uygulama IMO'sunda daha güvenli tutuyorsunuz. Aynı satırlar boyunca başka bir yaklaşım, uygulama düzeyinde insan okunabilirliği (¤ = para birimi simgesi) için ¤unit.subunit biçiminde biçimlendirme ile [uzun] bir tamsayı kullanmaktır.


1

SQL Server'daki para veri türü ondalık basamaktan sonra dört basamak içerir.

SQL Server 2000 Çevrimiçi Kitaplar'dan:

Parasal veriler, pozitif veya negatif para miktarını temsil eder. Microsoft® SQL Server ™ 2000'de, parasal veriler para ve küçük para veri türleri kullanılarak saklanır. Parasal veriler dört ondalık basamağa kadar saklanabilir. Değerleri -922,337,203,685,477,5808 ile +922,337,203,685,477,5807 (bir değeri saklamak için 8 bayt gerektirir) aralığında depolamak için para veri türünü kullanın. -214,748.3648 ile 214,748.3647 arasındaki değerleri saklamak için küçük para veri türünü kullanın (bir değeri saklamak için 4 bayt gerekir). Daha fazla sayıda ondalık basamak gerekiyorsa, bunun yerine ondalık veri türünü kullanın.


1

Bazen bir kuruşdan daha azına gitmeniz gerekebilir ve çok büyük demonasyonlar kullanan uluslararası para birimleri vardır. Örneğin, müşterilerinize işlem başına 0,088 sent ücret uygulayabilirsiniz. Oracle veritabanımda sütunlar NUMBER (20,4) olarak tanımlandı


1

DB'de herhangi bir aritmetik işlem yapacaksanız (faturalandırma oranlarını vb.), Muhtemelen buradaki insanların önerdiğinden çok daha fazla hassasiyet isteyeceksiniz, aynı şekilde asla uygulama kodunda çift kesinlikli kayar nokta değerinden daha az bir şey kullanmak istemek


Düşündüğüm buydu, ama döviz kurları açısından (yani Zimbawe doları USD'ye dönüştürmek). Çok küçük ondalık sayılarda yuvarlamayı nasıl ele aldıklarını görmek için kullandığım veritabanlarında (psql, sqlite) bazı deneyler yapacağım.
Ivan

Ayrıca, şamandıraların bazı dbms / dillerde doğruluk sorunları yok mu?
Ivan

2
şamandıraların TÜM dillerde doğruluk sorunları vardır.
Marcus Downing

Bu günlerde en yaygın öneri keyfi hassasiyet (düşünmek BigDecimal) kullanmaktır, ancak uzun süre çift duyarlıktı ( doubleyerine düşünmek float). Ayrıca, keyfi hassasiyet bazı durumlarda önemli performans cezalarına sahiptir. Test kesinlikle doğru bir yaklaşımdır.
Hank Gay

0

IBM Informix Dynamic Server kullanıyorsanız, DECIMAL veya NUMERIC türünde küçük bir varyant olan bir MONEY türünüz olur. Her zaman bir sabit nokta tipidir (oysa DECIMAL bir kayan nokta tipi olabilir). 1'den 32'ye kadar bir ölçek ve 0'dan 32'ye kadar bir hassasiyet (varsayılan olarak 16 ölçeğine ve 2'ye kadar bir hassasiyete) belirleyebilirsiniz. Bu nedenle, neyi depolamanız gerektiğine bağlı olarak, ABD Federal Açığını en yakın kuruşa kadar tutacak kadar büyük DECIMAL (16,2) kullanabilirsiniz veya daha küçük bir aralık veya daha fazla ondalık basamak kullanabilirsiniz.


0

Büyük bir kısmı için sizin veya müşterinizin gereksinimlerinin hangi hassasiyet ve ölçeğin kullanılacağını belirlemesi gerektiğini düşünürüm. Örneğin, sadece GBP cinsinden parayla uğraşan bu e-ticaret sitesi için Ondalık olarak tutmam gerekiyordu (6, 2).


0

Burada geç bir cevap, ama kullandım

DECIMAL(13,2)

ki bu da doğru düşünmemin 99,999,999,999,99'a kadar izin vermesidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.