Skip to content

Cosmos: Warn when decimals is mapped without a value converter (imprecise arithmetic) #38137

@JoasE

Description

@JoasE

Bug description

You are able to model an entity with a decimal property, while cosmos stores all floating point numbers as doubles. This can cause data and accuracy loss, aswell as unexpected rounding errors in mathematical expressions.

Your code

using Microsoft.EntityFrameworkCore;

var options = new DbContextOptionsBuilder<MyDbContext>()
    .EnableSensitiveDataLogging()
    .UseCosmos("AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", "test")
    .Options;

using var context = new MyDbContext(options);
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

var entity = new Product
{
    TaxRate = 1/6m,
    Compound1 = 0.1m,
    Compound2 = 0.2m,
    BigDecimal = 12345678901234567890.0m
};

context.Products.Add(entity);
await context.SaveChangesAsync();
context.ChangeTracker.Clear();

// Round-trip the entity and observe accuracy loss
var loaded = await context.Products.SingleAsync();
Console.WriteLine();
Console.WriteLine($"Original TaxRate: {entity.TaxRate}");
Console.WriteLine($"Loaded TaxRate:   {loaded.TaxRate}");
Console.WriteLine($"TaxRate match:    {loaded.TaxRate == entity.TaxRate}"); // False

// Projection with mathematical operations suffers from rounding errors
var projected = await context.Products
    .Select(p => (p.Compound1 + p.Compound2) * 100000000000000000)
    .SingleAsync();

var expectedProjected = (0.1m + 0.2m) * 100000000000000000;

Console.WriteLine();
Console.WriteLine($"Expected projection (decimal math): {expectedProjected}");
Console.WriteLine($"Projected (Cosmos math): {projected}");
Console.WriteLine($"Projection match: {projected == expectedProjected}"); // False

Console.WriteLine();
Console.WriteLine("Big decimal not stored correctly:");
Console.WriteLine($"Original BigDecimal: {entity.BigDecimal}");
Console.WriteLine($"Loaded BigDecimal:   {loaded.BigDecimal}");
Console.WriteLine($"BigDecimal match:    {loaded.BigDecimal == entity.BigDecimal}"); // False

public class MyDbContext(DbContextOptions options) : DbContext(options)
{
    public DbSet<Product> Products { get; set; } = null!;
}

public class Product
{
    public int Id { get; set; }
    public decimal TaxRate { get; set; }
    public decimal Compound1 { get; set; }
    public decimal Compound2 { get; set; }
    public decimal BigDecimal { get; set; }
}

Output

Original TaxRate: 0.1666666666666666666666666667
Loaded TaxRate:   0.166666666666667
TaxRate match:    False

Expected projection (decimal math): 30000000000000000.0
Projected (Cosmos math): 30000000000000004
Projection match: False

Big decimal not stored correctly:
Original BigDecimal: 12345678901234567890.0
Loaded BigDecimal:   12345678901234600000
BigDecimal match:    False

EF Core version

10.0.0

Database provider

Microsoft.EntityFrameworkCore.Cosmos

Target framework

.NET 10

Operating system

W11

IDE

VS2026

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions