You found a bug! We've classified it as EXTGWT-3223 . We encourage you to continue the discussion and to find an acceptable workaround while we work on a permanent fix.
  1. #1
    Ext GWT Premium Member
    Join Date
    Dec 2011
    Location
    Earth
    Posts
    243
    Vote Rating
    1
    nbuesing is on a distinguished road

      0  

    Default BigDecimalField and "double/float" garbage

    BigDecimalField and "double/float" garbage


    GXT 3.0.5/3.0.6
    GWT 2.4
    Browser: IE8 [ dev mode ] and Chrome (latest) [ production mode ]

    This issue did NOT happen in GXT 3.0.4, but happens in GXT 3.0.5

    Enter a value in the BigDecimal Field and then have the field loose focus (hit tab). The ValueChangeEvent event.getValue() will result in a BigDecimal with lots of "garbage".

    With GXT 3.0.4 I enter 11.55, I get a BigDecimal of 11.55. On GXT 3.0.5 or 3.0.6, I get
    11.5500000000007105...

    Sample application attached (and screenshot from test code).

    BigDecimalField.png
    BigDecimalFieldIssue.zip

  2. #2
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,734
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    Reviewing history, a change was made in *parsing*, but not in rendering between those two releases which fixed a locale issue, and possibly contributed to this test case. At a guess, in order to use the NumberFormat, the parse code turns the string into a double (using NumberFormat.parse), and that ieee754 double has the extra floating point garbage at the end, which is then passed to a BigDecimal. In 3.0.4 and earlier, we used the BigDecimal constructor that accepted a string, but this was changed to correctly support other locales.

    If this is indeed the change, then we're stuck between a rock and a hard place - avoid ieee754 number uglyness, or correctly parse numbers that use something other than a . as a decimal separator.

  3. #3
    Ext GWT Premium Member
    Join Date
    Dec 2011
    Location
    Earth
    Posts
    243
    Vote Rating
    1
    nbuesing is on a distinguished road

      0  

    Default


    Quote Originally Posted by Colin Alworth View Post
    If this is indeed the change, then we're stuck between a rock and a hard place - avoid ieee754 number uglyness, or correctly parse numbers that use something other than a . as a decimal separator.
    Isn't the point of Java's BigDecimal is to avoid ieee754 uglyness? So, it would be my preference that if BigDecimal is being used, then ieee754 uglyness shouldn't be something the developer has to worry about.

  4. #4
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,734
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    That's exactly the point of BigDecimal - but with the BigDecimal(String) constructor, only locales that use . are supported. Since NumberFormat only can parse values into doubles, that is the strongest set of possible values that can be accepted. Notice that this means that BigIntegerField and LongField will likely have the same issue.

    If there was a built-in way (in Java that didn't required java.text, or in GWT) to parse a String to a BigDecimal in a i18n-able way, we would (or at least should) be using that. There doesn't seem to be such a way, short of either re-implementing the number parsing logic for other types, or some ugly/hacky regex work.

    Edit: Our problem is that we are in trouble either way, though we'll think about changing this for the next release. You can change this locally by using the 3.0.4 BigDecimalPropertyEditor in GXT 3.0.5 and beyond.

  5. #5
    Ext GWT Premium Member
    Join Date
    Dec 2011
    Location
    Earth
    Posts
    243
    Vote Rating
    1
    nbuesing is on a distinguished road

      0  

    Default


    Colin,

    I had already switched to our BigDecimalPropertyEditor, thanks. Once I realized that was the issue, I reverted back to the our version that we used prior to GXT having one (I believe someone actually brought my version into GXT back during beta).

  6. #6
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,734
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    I've filed this internally for us to review, but short of building our own number parser, I'm not sure what steps we'll be able to take in this direction. We'll update this thread with any decisions that are made.

  7. #7
    Sencha User
    Join Date
    Jul 2011
    Posts
    138
    Vote Rating
    13
    Andreas Samjeske will become famous soon enough

      0  

    Default


    Just ran into this (again). Any news or workarounds?

    Hard to believe this issue has been filed more than 4 years ago:
    http://code.google.com/p/google-web-...detail?id=4685

  8. #8
    Sencha - GXT Dev Team
    Join Date
    Jan 2012
    Location
    Arlington, WA
    Posts
    484
    Vote Rating
    15
    branflake2267 will become famous soon enough

      0  

    Default


    This may be of interest, using a BigDecimal value provider. See what you think of its use in this test case. This is using GXT 3.0.6.

    Code:
    import java.math.BigDecimal;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    
    import com.google.gwt.core.client.GWT;
    import com.google.gwt.editor.client.Editor.Path;
    import com.google.gwt.i18n.client.DateTimeFormat;
    import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
    import com.google.gwt.i18n.client.NumberFormat;
    import com.google.gwt.safehtml.shared.SafeHtml;
    import com.google.gwt.safehtml.shared.SafeHtmlUtils;
    import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.google.gwt.user.client.ui.Widget;
    import com.sencha.gxt.cell.core.client.SimpleSafeHtmlCell;
    import com.sencha.gxt.core.client.ValueProvider;
    import com.sencha.gxt.data.shared.ListStore;
    import com.sencha.gxt.data.shared.ModelKeyProvider;
    import com.sencha.gxt.data.shared.PropertyAccess;
    import com.sencha.gxt.widget.core.client.FramedPanel;
    import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
    import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
    import com.sencha.gxt.widget.core.client.container.Viewport;
    import com.sencha.gxt.widget.core.client.form.CheckBox;
    import com.sencha.gxt.widget.core.client.form.DateField;
    import com.sencha.gxt.widget.core.client.form.DateTimePropertyEditor;
    import com.sencha.gxt.widget.core.client.form.NumberField;
    import com.sencha.gxt.widget.core.client.form.NumberPropertyEditor;
    import com.sencha.gxt.widget.core.client.form.TextField;
    import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
    import com.sencha.gxt.widget.core.client.grid.ColumnModel;
    import com.sencha.gxt.widget.core.client.grid.Grid;
    import com.sencha.gxt.widget.core.client.grid.editing.GridEditing;
    import com.sencha.gxt.widget.core.client.grid.editing.GridInlineEditing;
    
    
    public class GridInlineEditingBigDecimalTabOutFieldTest {
    
    
      public GridInlineEditingBigDecimalTabOutFieldTest() {
        VerticalLayoutContainer vlc = new VerticalLayoutContainer();
        vlc.add(createGrid(), new VerticalLayoutData(1, 1));
    
    
        Viewport vp = new Viewport();
        vp.add(vlc);
        RootPanel.get().add(vp);
      }
      
      interface PlaceProperties extends PropertyAccess<Plant> {
        ValueProvider<Plant, Date> available();
    
    
        @Path("id")
        ModelKeyProvider<Plant> key();
    
    
        ValueProvider<Plant, String> light();
    
    
        ValueProvider<Plant, String> name();
    
    
        ValueProvider<Plant, Boolean> indoor();
    
    
        ValueProvider<Plant, BigDecimal> price();
      }
    
    
      private static final PlaceProperties properties = GWT.create(PlaceProperties.class);
    
    
      protected Grid<Plant> grid;
      private FramedPanel panel;
      private ListStore<Plant> store;
    
    
      public Widget createGrid() {
        if (panel == null) {
          final NumberFormat globalFormat = NumberFormat.getFormat("#,###,###,###,##0.00"); 
          
          ColumnConfig<Plant, String> nameColumn = new ColumnConfig<Plant, String>(properties.name(), 220, "Name");
          ColumnConfig<Plant, BigDecimal> priceColumn = new ColumnConfig<Plant, BigDecimal>(properties.price(), 100, "Price");
          ColumnConfig<Plant, String> lightColumn = new ColumnConfig<Plant, String>(properties.light(), 130, "Light");
          ColumnConfig<Plant, Date> dateColumn = new ColumnConfig<Plant, Date>(properties.available(), 95, "Date");
          ColumnConfig<Plant, Boolean> indorColumn = new ColumnConfig<Plant, Boolean>(properties.indoor(), 55, "Indoor");
         
          priceColumn.setCell(new SimpleSafeHtmlCell<BigDecimal>(new AbstractSafeHtmlRenderer<BigDecimal>() {
            @Override
            public SafeHtml render(BigDecimal object) {
              return SafeHtmlUtils.fromString(globalFormat.format(object));
            }
          }));
          
          List<ColumnConfig<Plant, ?>> l = new ArrayList<ColumnConfig<Plant, ?>>();
          l.add(nameColumn);
          l.add(priceColumn);
          l.add(lightColumn);
          l.add(dateColumn);
          l.add(indorColumn);
    
    
          ColumnModel<Plant> cm = new ColumnModel<Plant>(l);
    
    
          store = new ListStore<Plant>(properties.key());
          store.setAutoCommit(false);
          store.addAll(getPlants());
    
    
          grid = new Grid<Plant>(store, cm);
          grid.getView().setAutoExpandColumn(nameColumn);
    
    
          // EDITING//
          DateField dateField = new DateField(new DateTimePropertyEditor(DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT)));
          dateField.setClearValueOnParseError(false);
          
          NumberField<BigDecimal> shippedQuantityField = new NumberField<BigDecimal>(new NumberPropertyEditor.BigDecimalPropertyEditor(globalFormat));
          
          final GridEditing<Plant> editing = new GridInlineEditing<Plant>(grid);
          editing.addEditor(nameColumn, new TextField());
          editing.addEditor(priceColumn, shippedQuantityField);
          editing.addEditor(lightColumn, new TextField());
          editing.addEditor(dateColumn, dateField);
          editing.addEditor(indorColumn,  new CheckBox());
          // EDITING//
          
          panel = new FramedPanel();
          panel.setHeadingText("Editable Grid Example");
          panel.setPixelSize(600, 400);
          VerticalLayoutContainer con = new VerticalLayoutContainer();
          con.setBorders(true);
          con.add(grid, new VerticalLayoutData(1, 1));
          panel.setWidget(con);
        }
    
    
        return panel;
      }
    
    
      private static int AUTO_ID = 0;
    
    
      public class Plant {
        private DateTimeFormat df = DateTimeFormat.getFormat("MM/dd/y");
    
    
        private int id;
        private String name;
        private String light;
        private BigDecimal price;
        private Date available;
        private boolean indoor;
        private String color;
        private int difficulty;
        private double progress;
    
    
        public Plant() {
          id = AUTO_ID++;
    
    
          difficulty = (int) (Math.random() * 100);
          progress = Math.random();
        }
    
    
        public Plant(String name, String light, BigDecimal price, String available, boolean indoor) {
          this();
          setName(name);
          setLight(light);
          setPrice(price);
          setAvailable(df.parse(available));
          setIndoor(indoor);
        }
    
    
        public double getProgress() {
          return progress;
        }
    
    
        public void setProgress(double progress) {
          this.progress = progress;
        }
    
    
        public String getColor() {
          return color;
        }
    
    
        public int getDifficulty() {
          return difficulty;
        }
    
    
        public void setDifficulty(int difficulty) {
          this.difficulty = difficulty;
        }
    
    
        public void setColor(String color) {
          this.color = color;
        }
    
    
        public Date getAvailable() {
          return available;
        }
    
    
        public int getId() {
          return id;
        }
    
    
        public String getLight() {
          return light;
        }
    
    
        public String getName() {
          return name;
        }
    
    
        public BigDecimal getPrice() {
          return price;
        }
    
    
        public boolean isIndoor() {
          return indoor;
        }
    
    
        public void setAvailable(Date available) {
          this.available = available;
        }
    
    
        public void setId(int id) {
          this.id = id;
        }
    
    
        public void setIndoor(boolean indoor) {
          this.indoor = indoor;
        }
    
    
        public void setLight(String light) {
          this.light = light;
        }
    
    
        public void setName(String name) {
          this.name = name;
        }
    
    
        public void setPrice(BigDecimal price) {
          this.price = price;
        }
    
    
        @Override
        public String toString() {
          return name != null ? name : super.toString();
        }
      }
    
    
      public List<Plant> getPlants() {
        List<Plant> plants = new ArrayList<Plant>();
        plants.add(new Plant("Bloodroot", "Mostly Shady", new BigDecimal(0), "03/15/2006", true));
        plants.add(new Plant("Columbine", "Shade", new BigDecimal(0), "03/15/2006", true));
        plants.add(new Plant("Marsh Marigold", "Mostly Sunny", new BigDecimal(0), "05/17/2006", false));
        plants.add(new Plant("Cowslip", "Mostly Shady", new BigDecimal(0), "03/06/2006", true));
        plants.add(new Plant("Dutchman's-Breeches", "Mostly Shady", new BigDecimal(6.44), "01/20/2006", true));
        plants.add(new Plant("Ginger, Wild", "Mostly Shady", new BigDecimal(9.03), "04/18/2006", true));
        return plants;
      }
    }
    Brandon